实习日记
猿辅导-初高中服务器B端研发实习生-陈宽
目录
-
实习日记
- play ground task 1
- May 30
- May 31
- June 4
- June 5
- June 06
- June 09
- June 10
- June 11
- June 12
- June 13
- June 14
- June 16
- June 19
- June 20
- June 21
- June 22
- June 25
- June 26
- June 27
- June 28 & 29
- July 2
- July 3
- July 4
- July 5
- July 6
- July 9 && July 10
- July 11
- July 12
- July 13
- July 14
- July 15
- July 17
- July 18
- July 19
- July 20
- July 21-26
- July 29
- July 31
- Aug 1
- Aug 2
- Aug 3
- Aug 4
- Aug 6
- Aug 7 && Aug 8
- Aug 9
- Aug 10
- Aug 12
- Aug 13-14
- Aug 15
- Aug 16
- Aug 17
- Aug 19-20
- Aug 21
- Aug 22
play ground task 1
logic && service
* Logic层和Service层有什么区别?Logic 处理数据,调用service , Service 调用DAO
RPC && REST
* Rpc接口和REST接口的用途有什么区别?是否可以统一?
REST : GET 获取数据/POST 更新or创建数据/ DELETE 删除数据 RPC 直接用thrift 框架/非常容易实现,然后再写一个RPCHandler 即可
* Spring注解中,@Bean和@Component有什么区别?
- @Component以及他的特殊化(@Controller, @Service 和 @Repository)允许在通过类路径扫描自动发现。@Bean却只能在配置类中明确的声明一个单例的bean。
*哪些地方使用了Spring AOP?没有
目前还没有QaQ
*数据存储除了mysql,还有什么可能的方案?Key--Value的nosql?
May 30
RestController
1. 注意@RestController 和@Controller的区分
Mac 关闭端口
2. MAC 查找并关闭8080 端口 查找8080端口
sudo lsof -i :8080
然后根据PID杀进程:
sudo kill -9 61342(即pid)
数据VO 的使用场景
3. create 对象: logic 和controller 返回VO, service和db返回 id
关于Test
4. 测试尽量使用 assertthat
Rest 使用规范
5. REST 规范: http://wangwei.info/about-rest-api/
===== 单元测试 =====.
- @Spy
private NamedParameterJdbcTemplate dbwriter = TestDbHelper.createNamedParameterJdbcTemplateInMemory(); @InjectMocks private DbTodoItemStorageImpl todoItemStorage = new DbTodoItemStorageImpl();
注意sql名称和规范
May 31
DataNotFoundException
1. complete tutor-playground RPC interface with thrift define.
when define the GET (by id) , need throw the DataNotFound Exception every time!
增加字段Optional
2. When add the new Attribute to the Object, in thrift , it should be optional!
Mac 历史命令 指令
3. tips :MAC查找历史命令~~~history |grep <command>
June 4
Query 的两种方式
spring sql query两种方式: 1. 用filter,在各个属性上建立filter(service层) 2. 使用条件condition判断,(db层)
June 5
安全获取userId等数据
1. userId 需要从SecHelper中获取,防止他人伪造userId
online分支 合并操作
2. 合代码的git操作:
git checkout master
git pull
git checkout online
git pull
git merge origin/master --log --no-ff
git commit --amend
git push online-review
DB 支持颜文字
3. Db支持U0001f60a颜文字等操作~~~: for example : tutorMentorApi.datasource.tomcat.initSQL: SET NAMES utf8mb4 (yaml)
CREATE TABLE amaze_feedback (
userId int(11) unsigned NOT NULL,
content text COLLATE utf8mb4_unicode_ci,
imageIds text CHARACTER SET utf8 NOT NULL,
createdTime bigint(20) NOT NULL, PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
项目部署流程
4.项目部署基本流程: 参考wiki dev/deploy https://wiki.zhenguanyu.com/Dev/Deploy
观察上线状态,注意主机是否重启成功。stage机器上完后,登陆跳板机(ssh liht01@access1), 以用户名tutor登陆到到相应的主机,查看/home/shared/log/下面的log文件,验证服务的状态。如果一切正常,点击"继续"来完成剩余机器的上线。
June 06
空值的判断和返回
1. 用前闭后开的区间来表示一个时间 Range
空值判断
2. String.isBlank(str); String.isNotBlank(str); ArrayUtils.isEmpty(arr); ArrayUtils.isNotEmpty(arr); CollectionUtils.isEmpty(coll);
返回空值
3. Collections.emptyMap(); Collections.emptyList(); Collections.emptySet(); StringUtils.EMPTY;
-- JMS :http://www.cnblogs.com/chenpi/p/5559349.html
June 09
导出CSV
Get skill : Java save data into CSV file
详见 tutor-address
注意,job 的日志一定要详细,有开始和结束!
job结束后,最好有一个validare job
June 10
参加公司TB~ U0001f604
June 11
导数剧过程
完成导出用户地址job,
要连接跳板机, 上传自己的jar包及相关文件 到自己的工作目录下面。此处可用软连接:ln -s 源文件 目标文件
关于上传文件: https://wiki.zhenguanyu.com/DEVOP/SEC/Mupload 随后使用wget 获取。
运行jar包:
nohup java -jar xxxxxx.jar --server.port=9399 --spring.profiles.active=online --logging.config=classpath:log4j2.yaml --captain.enabled=false --job.active=XXXXXXXXXXXXXJob --fix=true > log/XXXXXXXXXXX.out
继续tutor-playground2, 测试了res接口, 对第一次提交的代码进行修改, 研究了redis的用法及配置。
June 12
上线了tutor-atm-lesson
更新数据时,LOG日志应该更详细一些, 第一次上传的job 没有 “job end ”信息, 差评!!!!!!!!!!!!!!
继续tutor-playground, 使用缓存时还是要好好考虑数据一致性的问题哈!
git 返回到最新的提交~git reset --hard origin/master
June 13
git 学习
git pull = git fetch + git merge
git pull --rebase = git fetch + git rebase
现在来看看git merge和git rebase的区别。
假设有3次提交A,B,C。
在远程分支origin的基础上创建一个名为"mywork"的分支并提交了,同时有其他人在"origin"上做了一些修改并提交了。
其实这个时候E不应该提交,因为提交后会发生冲突。如何解决这些冲突呢?有以下两种方法:
git merge
用git pull命令把"origin"分支上的修改pull下来与本地提交合并(merge)成版本M,但这样会形成图中的菱形,让人很困惑。
git rebase
创建一个新的提交R,R的文件内容和上面M的一样,但我们将E提交废除,当它不存在(图中用虚线表示)保持提交曲线为直线,让大家易于理解。
在rebase的过程中,有时也会有conflict,这时Git会停止rebase并让用户去解决冲突,解决完冲突后,用git add命令去更新这些内容,然后不用执行git-commit,直接执行git rebase --continue,这样git会继续apply余下的补丁。在任何时候,都可以用git rebase --abort参数来终止rebase的行动,并且mywork分支会回到rebase开始前的状态。
git reset
git reset HEAD 回退版本,一个表示一个版本,可以多个,另外也可以使用 git reset HEAD~n这种形式。
git reset --hard HEAD~1 更多命令 https://www.yiibai.com/git/git_reset.html hard 会删除上次提交之后的东西, soft不会?
假设你已经添加了一个文件进入索引,但是而后又不打算把这个文件提交,此时可以使用git reset把这个文件从索引中去除
$ git reset -- frotz.c (1)
$ git commit -m "Commit files in index" (2)
$ git add frotz.c
June 14
今天主要做了 mentorAdvice 的相关操作工作
jsonCreater
@JsonCreator 的包要注意!!!
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
June 16
缓存数据库一致性问题 callback解决
主要看了redisson 和 db 操作,使用callback 解决数据不一致问题
June 19
Episode
可参考: https://wiki.zhenguanyu.com/Livecast/MicroService/TutorEpisode#episode
June 20
熟悉了评价相关的业务。
Charles
学会使用Charles 进行 mac 和 ios 端 请假捕获和抓包分析, 参考blog : http://blog.devtang.com/2013/12/11/network-tool-charles-intr/
遇到问题:需要Mac和iOS 安装相关证书,安装后,需要授权信任证书, 否则 网页或者app将无法打开。
此外,网页端url 请求应该是统一处理过,无法查看, 但是app 端可以进行url查看
June 21
本地环境常用参数配置
--spring.profiles=test
--logging.config=classpath:log4j2.yaml
--rpcServer.port=5110
--captain.restfulPort=6110
--actuator.enabled=false
--logging.access.directory=/Users/chenkuan/Desktop/Yuanfudao
Git Cherry Pick 操作
Master:
git ll 找到commit 记下
git checkout online
git pull git cherry-pick a7099c3
git commit --amend
判空 和 分段读取
习惯性的对集合判空
---
使用RPC时尽量分段读取
Tip MapUtilsEx.convertValue
June 22
今天写业务
June 25
业务
June 26
导出数据时,尽量分段导出。
June 27
学习清理服务
学习了如何了进行服务清理: 检查服务依赖,查看日志文件>>>>删除代码>>>>修改nginx>>>>删除服务实例 参考连接: https://wiki.zhenguanyu.com/Livecast/B/ServiceClean
June 28 & 29
进行服务清理,好像没遇到坑
索引 ES
http://www.baeldung.com/lucene
https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html
July 2
cache key
使用的时候注意,不要忘记id !!!!!!!!! 今天是一个惨痛的教训
July 3
service 上线时候记得刷新,
另外又学会了 git revert--->git commit --amend --->git push
July 4
Tips: 自动引入Jar包时也要看看版本,某些低版本功能可能不完善,比如 StringUtils.isNumberic 不同版本就有不一样的处理
July 5
今天第一次发布Maven 包
maven clean deploy
July 6
今日有红牛,JavaCpp 有点难。。。
1. 预习新牛课程
[Pro Git](https://git-scm.com/book/zh/v2) 前3章,尤其是第3章*分支*
July 9 && July 10
复习Git, 上线业务
学习了 Maven 和 shell
https://www.jianshu.com/p/e1c8e5bfa45e
http://www.infoq.com/cn/minibooks/maven-in-action
对Maven 有了进一步的了解,以前只会用但没有详细了解过。 对shell也有了大致的了解,可以看懂代码了,有机会自己写个东西玩玩
July 11
上午scrum 会, 下午完成了两个task, 顺便读了读 Java 8 实战
July 12
git reset 三种
https://www.cnblogs.com/kidsitcn/p/4513297.html
新牛学习--Git,MVN,shell
* Git如何存储数据 * Snapshot vs Diff Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方法。 概念上来区分,其它大部分系统以文件变更列表的方式存储信息。 这类系统(CVS、Subversion、Perforce、Bazaar 等等)将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。 Git 不按照以上方式对待或保存数据。 反之,Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流。 * 如何存储目录? * 如何保证数据完整性 Git 中所有数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。 Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希看起来是这样: 24b9da6552252987aa493b52f8696cd6d3b00373 Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值。 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。 * 文件状态 * 三个区域 * 工作区 working directory * 暂存区 staging area * 仓库 repo * git reset的不同类型 git 的三种状态 和 reset感觉灰常有意思,棒! 主要是Hard(三个指针都移动), Soft(暂存区和仓库移动) 和Mixed(仓库移动) --- ### Git Workflow * branch和commit的关系 * 在gerrit上提交一个review,工作流程是怎么样的 * 如果把分支A提到了分支B,会发生什么 * rebase和merge的机制和适用场景 * github的工作流程* * 版本管理工具的变迁* --- ### Maven * [幻灯片](https://wiki.zhenguanyu.com/fankai?action=AttachFile&do=view&target=Maven%E7%AE%80%E4%BB%8B.pdf) * 作为项目的对象模型 * 坐标与版本 * SNAPSHOT版本的意义 * Maven的生命周期(clean default site (default 为主)) * 每个生命周期是一组有序的阶段 validate->compile->test->package->verify->install->deploy * 解释mvn clean package的意义 * Maven依赖管理 * 仓库的概念 (中央仓库,公司仓库) * 依赖范围 compile 默认,编译,测试,运行都需要 provided 只在编译和测试时需要 runtime 只在测试和运行的时候需要 test 只在测试时候需要 * 如何确定依赖的版本 1. 显示确定依赖版本 2 DependencyManageMent中定义的版本 3. 传递依赖路径中长度最短的版本 * Maven常用命令 mvn dependency:list mvn dependency:tree mvn dependency:analyze --- ### Shell脚本 * Shell基础 * 变量 * 运算符 * 控制流 * Shell脚本的适用场景 * Shell脚本如何实现后台运行、重定向、并发等功能 * 常用文本处理工具 --- ### UNIX 设计原则 * Controlling complexity is the essence of computer programming. * Keep It Simple, Stupid! --- ### 代码规范 * 代码规范的原则和意义 * Programs must be written for people to read, and only incidentally for machines to execute. - SICP * 保持代码风格的一致性比起追求最好的风格更加重要 * 重构是保持代码质量的重要手段 ---
July 13
tutor-team服务整理
July 14
周末来公司看了下 java 8 实战, 1-4 章。
行为参数化(传递代码)->匿名类->Lambda表达式->使用方法引用
引入流的概念,stream 内部有些像流水线式工作
July 15
新牛--Java
## 学习目标 * 掌握Java语言的常用特性,包括 * 异常处理机制 * 类型信息和反射机制 * 泛型的原理和使用 * 注解 * 了解Java 8引入的新特性 * Lambda表达式 * Stream API
## 异常 * 异常 vs 错误码 * Java异常的分类和层次 * Checked Exception vs Unchecked Exception * 各自有什么优缺点 * 如何保证释放资源 * 如果在catch或者finally块里抛出异常会如何? 上一个异常会被吞掉 * 如果在构造函数中跑出异常会如何? 不要这么做,会=有可能会使程序无法执行,也会增加上层编程人员的开发难度 * try-with-resource * 会释放资源 * 什么叫suppressed exception,如果保留 * 永远不要吞异常!
### 类型信息RTTI * 类型也是一个对象 * 获取和使用获取类型信息 * 向下转型 downcast * instanceof 操作符 * isInstance() 方法 * typeid and typeinfo in c++ * * 反射 * 动态代理 * 什么是代理模式 * 动态代理的作用 AOP用到
### 泛型 * 泛型和继承 * 协变 covariance * 逆变 contravariance * 不变 invariance ~~~ if a Derived is a Base, then a Collection<Derived> is a Collection<Base> ? or a Collection<Base> is a Collection<Derived> ? ~~~ * 泛型数组? 集合是不变的,数组是协变的 --- ### 泛型 * 泛型和继承 ~~~ List<Object> objs = new ArrayList<Integer>(); objs.add(3); objs.add("hello"); ~~~
### 注解 * 注解如何发挥作用 * 各个元注解的意义 *
### Lambda表达式 * 从匿名类到Lambda表达式 * 闭包 * 有什么区别? * 函数式接口 * 函数不是一等公民(first class) * 常用接口
### Stream基本操作# * 不修改数据源 * 延迟操作 (lazy execution) * 副作用 (side effect) * 常用操作 * Optional如何解决NPE * Optional链式操作
July 17
git 修改之前提交过的内容
git log –oneline 查看head git rebase -i xxxxxxx回到想修改的提交之前 选择进行edit 进行修改 git add . git commit –amend (依然使用原来的提交) git rebase –continue 结束
shell
grep 'duration=[0-9][0-9]' http-request.log | cut -f4 |sort |uniq -c|sort -nr >result grep 'products/342' http-request.log | cut -f1 -d :|uniq -c >result1
作业里学到的两个shell 命令
July 18
Test 小坑
注意不可以 Test method为private
July 19
新牛学习--JVM 深入了解
### JVM * VM的概念和作用 * JVM vs Java * 其他VM ---
### 内存数据区域 * 有哪些数据区域 1. 程序计数器,在虚拟机概念模型里,字节码解释器工作时候就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支循环,跳转,异常处理,线程恢复等基础功能,都需要依赖这个计数器完成。 为了线程切换的能恢复到正确的执行位置,每条线程都需要又一个独立的程序计数器,在线程私有内存中。 2. Java 虚拟机栈 , 与程序计数器一样,这也是线程私有的,生命周期与线程相同,描述的是Java方法执行的内存模型,每个方法在执行的时候都会创建一个Stack Frame,栈桢存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用到执行的过程,就对应了一个栈桢在虚拟机栈中从入栈到出栈的过程。 3. 本地方法栈。 与虚拟机栈发挥的作用比较相似,他们之间的区别不过是虚拟机栈为虚拟机执行Java方法,也就是字节码的服务,本地方法栈则为虚拟机使用的Native方法服务,有的虚拟机Sun HotSpot 会把这两个合二为一。 4. Java 堆, 垃圾回收的主要区域,绝大部分对象都会分配到堆上。 堆中还可细分为新生代和老年代,Eden 区,From Survivor吗, To Survivor, 老年代。 线程共享的Java堆中还会分出多个线程私有的分配缓冲区域,TLAB。 映射在物理上可以不连续,只要逻辑上是连续的就行。 5. 方法区, 与Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。 有些人也把这里叫做永久代。无法满足分配时会出发OOM。 方法区还有个叫做运行常量池的地方,存放编译期生成的各种字面量和符号引用。 * 堆和栈 * 直接内存/Direct Memory 直接内存: 不是Java虚拟机运行时的数据区域,但是也被频发使用,也可能导致OOM。 和NIO关联密切,Channel 与缓冲区,的IO方式,可以使Native函数库直接操作分配堆外内存,通过一个存储在Java堆中的DirectByBuffer 对象作为这块内存的引用,避免来回复制数据,显著提高性能。 * 对象创建过程 * 新建对象的方式 先去常量池检查这个符号引用代表的类是不是已经被加载,解析和初始化过。 如果没有先执行相应的类加载过程 类加载检查通过后,虚拟机为对象进行分配内存,这里有“指针碰撞” 和 “空闲链表两种方法” 分别对应Java GC 的不同垃圾回收机制 ,compact 和 sweep 分配完后,对对象进行必要的设置,例如是哪个类的实例, 对象的Hash, 对象的GC分代年龄信息。 这些做完之后,从Java程序员的角度 新建才刚刚开始,所有字段还都是零。 随后进行初始化。 * 如何处理并发? 1. 使用CAS 平分配方式,保证更新操作。 2. 另一种是把内存分配的动作, 按照线程划分在不同的区域,TLAB。 * 对象内存布局 * 对象头 header * Mark Word和类型指针 (对象Hash 码, 对象分代年龄 锁记录指针,重量级锁指针) * 实例数据 instance data 真正存储的信息,各个定义的字段 * 对齐填充 padding 8字节整数倍
### 垃圾收集 * 概念和作用 * 引用计数算法 * 可达性分析算法 * GC Roots 1. 虚拟机栈中引用的对象 2 方法区中,类静态属性引用的对象 3 方法区中, 常量引用的对象 4 本地方法栈中JNI引用的对象 * 引用类型 强引用, 引用在就永远不会被回收 软引用,在系统即将要发生内存溢出异常之前,会把这些对象列进回收范围中进行二次回收 弱引用, 关联的对象只能生存到下一次垃圾收集之前,垃圾回收的时候,无论当前内存是否足够,都会回收掉只被弱引用关联的对象 虚引用, 无法通过虚引用获取对象实例,唯一目的就是对象回收时候会获得一个通知。 * 方法区需要回收吗? 需要, 废弃的常量和无用的类,(没有实例, 加载该类的ClassLoader已经回收,无法在任何位置通过反射访问该类的方法) * 常用收集算法 * 标记-清除 Mark-Sweep * 复制算法 * 标记-整理 Mark-Compact * 分代收集 * 新生代和老年代 * 永久代(Permanent Generation)和Metaspace * 安全点和安全区域 安全区域解决线程sleep blocked无法中断的问题,提供一块区域,到安全区挂起。
### 垃圾收集器 * 常用收集器 * Serial Stop the world 关掉所有线程, 优点,1. 简单高效,适合单核处理器,2 。 没有线程交互开销 * Serial Old * ParNew ParNew /SERIAL OLD 模型 * Parallel Scavenge 侧重于吞吐量 ,控制最大垃圾收集停顿时间-XX:MaxGCPauseMillis 直接设置吞吐量大小 -XX:GCTImeRatio 新生代收集器 *Parallel Old Parallel Old Parallel Scavenge 的老年版本 * CMS 目标:获取最短停顿时间 初始标记(标记GC直接关联的对象),并发标记(进行GC root tracing),重新标记(标记期间,用户程序继续运行的改动), 前两个阶段仍然是stop the world 缺点: 对CPU资源要求敏感,无法处理浮动垃圾(运行时产生的垃圾),采用标记清除会产生碎片 * G1 * Minor GC/Major GC/Mixed GC/Full GC * jstat里列出的GC指的是STW * gc log则是列出了所有的信息 * 垃圾收集与RAII的比较 *
{{
* 结构
- 魔数与版本
- 常量池
- 访问标志
- 类索引、父类索引与接口索引
- 字段表
- 方法表
_ }}
主要就是学习到了其他语言的一些编程思想
主要是team 相关资源的 redis crud操作。
《设计数据密集型应用》前三章, SST 和 LSM tree
熟悉提供单,命名规则等
今天再次有新建Job的需求,又遇到了IDE的坑。 解决办法: 把原代码push上去, 删除本地代码,再pull回来。 蜜汁解决,应该是IDE的毛病?
主要是写导数据以及验证结果的job, 发现了以前写的代码有点坑, 漏了注解,以及有的提交没法运行, 还有配置文件没有配全。 以后要保证不光可以编译通过,也可以跑起来!!!
应改会和 Aug 9 一起整理。
---
坑了, 代码先上了测试,没有改数据库。以后先记得把数据库换掉。
写日志要规范,多参考其他人的代码。
先合代码,再release 版本
scrum会议,月会,写业务,无坑。 公司很有发展前景。
LessonProduct.productId 改为lesson.productId
明天办理离职,时间过的很快,我很喜欢这里的工作氛围,有些不舍。
1. 学习tutor-playground 6 天 spring框架构建,采用标准的[三层架构] , Thrift, Rpc, RestApi, Mysql 存储, 单元测试, Redisson 实现缓存, 主要是数据一致性。 2. 增加服务 AmazeFeedback , 建表, CRUD操作, Http Api tutor-atm-user 3. 写Job 导出用户地址。 tutor-atm-address 4. 使用消息队列, 班课过期后,随材匹配自动变为暂停推单 tutor-atm-lesson 5. 更新tutor-at-lesson 线上数据 班课过期后,随材匹配自动变为暂停推单 tutor-atm-lesson 6. 服务端 - 导出运单列表增加『学生 id』 tutor-inventory-admin 7. 增加辅导老师带班建议 tutor-atm-user' 8. 服务端 - LessonVO 增加『剩余名额』 tutor-atm-lesson 9. 老师好评率数据计算 tutor-atm-user 10. 服务整理 use tutor-lesson instead of tutor-lesson-admin 11. 导出学生统计数据 成Excel Tutor-atm-stat 12. 服务端 - LessonVO 增加『首季用户占比』『首季用户续报率』『非首季用户续报率』 tutor-atm-lesson 13. 删除Http api tutor-episode-template/material tutor-atm-lesson 14. 去掉tutor-mentor-admin 依赖 15. 辅导老师身份问题修复 tutor-student-profile tutor-student-episode 16. 删除项目 tutor-lesson-admin 17. 修复导入金币Excel 错误 tutor-atm-episode 18. 整合ACL 19. 服务整理 InventoryAdminProxy Rpc 20. inventory query 接口支持 name 模糊匹配 tutor-inventory-admin 21. tutor-stat 改用 tutor-student-product 提供的接口 22. Team资源迁移(三周) 23. atm-episode 线上导出讨论区日志(支持多个小班) 24. 班课管理 - 班课支持原价配置 - 服务端(加字段, 加验证逻辑,导入数据刷缓存) 25. 取消lessonproduct依赖 ### 类加载
* 加载过程
* 加载
* 连接 - 验证,准备,解析
* 初始化
* <clinit>()和<init>()
* 使用
* 卸载
* 虚拟机严格规定了初始化的场景(主动引用)
* 类加载器的双亲委派模型
* 怎么判断两个类相同
### 执行字节码
* 栈帧
* 局部变量表
* 操作数栈
* 返回地址
* 方法调用
* 解析调用
* 分派调用
* 静态 vs 动态
* 单分派 vs 多分派
* 基于栈 vs 基于寄存器
### 编译
* AOT
* 前端/优化器/后端
* Javac
---
### 运行时优化
* 解释器与编译器并存
* 分层编译
* Server模式和Client模式*
* 即时编译 JIT
* 热点探测 Hot Span Detection
* 栈上替换 On Stack Replacement
* 运行期优化
---
### 编译器 vs 解释器 vs 虚拟机
Override 动态分配
Overload 静态分配
参数静态分配
前面调用的是动态分配
July 20
集体学习 JVM调优
调优目的: 吞吐量, 延迟,内存占用
调优参数:性能参数,行为参数,调试参数
调优远离: 和 垃圾回收机制有关
-Xmn ->新生代内存
-Xms ->JVM 启动时候可申请的最小内存
-Xmx ->JVM 可申请的最大heap内存
-XX: SurvivorRation->Eden 和S0 或者 S1 区的比例
-XX: NewRatio 新生到和老年代比例
-XX: NewSize 新生代空间
-XX: MaxTenringThreshold: 对象进入老年的年龄阈值
-XX:-UseSerialGC, serial&serial old (client默认模式)
-XX:-UseParallelGC, parallel scavenge& parral old server默认模式
-XX:-UseConcMarkSweepGC, ParNew+CMS
XX:+UseG1GC
jps: 查看jvm进程
jmap:提供 JVM 内存使用信息,适用于脚本中。
jmap
用于显示当前Java堆和永久代的详细信息(如当前使用的收集器,当前的空间使用率等)
-dump:生成java堆转储快照
-heap:显示java堆详细信息(只在Linux/Solaris下有效)
-F:当虚拟机进程对-dump选项没有响应时,可使用这个选项强制生成dump快照(只在Linux/Solaris下有效)
-finalizerinfo:显示在F-Queue中等待Finalizer线程执行finalize方法的对象(只在Linux/Solaris下有效)
-histo:显示堆中对象统计信息
-permstat:以ClassLoader为统计口径显示永久代内存状态(只在Linux/Solaris下有效
命令格式:jmap [option] vmid
jinfo:访问 JVM 系统属性,同时可以动态修改这些属性。
jstack:提供 Java 进程内的线程堆栈信息。
jstack
用于生成当前JVM的所有线程快照,线程快照是虚拟机每一条线程正在执行的方法,目的是定位线程出现长时间停顿的原因。
-F:当正常输出的请求不被响应时,强制输出线程堆栈
-l:除堆栈外,显示关于锁的附加信息
-m:如果调用到本地方法的话,可以显示C/C++的堆栈
命令格式:jstack [option] vmid
jstat:提供 Java 垃圾回收以及类加载信息。
jstat可以实时显示本地或远程JVM进程中类装载、内存、垃圾收集、JIT编译等数据
-class:监视类装载、卸载数量、总空间及类装载所耗费的时间
-gc:监听Java堆状况,包括Eden区、两个Survivor区、老年代、永久代等的容量,以用空间、GC时间合计等信息
-gccapacity:监视内容与-gc基本相同,但输出主要关注java堆各个区域使用到的最大和最小空间
-gcutil:监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-gccause:与-gcutil功能一样,但是会额外输出导致上一次GC产生的原因
-gcnew:监视新生代GC状况
-gcnewcapacity:监视内同与-gcnew基本相同,输出主要关注使用到的最大和最小空间
-gcold:监视老年代GC情况
jconsole:提供 JVM 活动的图形化展示,包括线程使用,类使用以及垃圾回收(GC)信息。
Java mission control
Memory analyzer tool
远程调试
-verbosegc配合使用的一些常用参数为:
-XX:+PrintGCDetails,打印GC信息,这是-verbosegc默认开启的选项
-XX:+PrintGCTimeStamps,打印每次GC的时间戳
-XX:+PrintHeapAtGC:每次GC时,打印堆信息
-XX:+PrintGCDateStamps (from JDK 6 update 4) :打印GC日期,适合于长期运行的服务器
July 21-26
Redis 业务学习
redis单线程
支持两种数据持久化方式:Snapshotting(快照)和 Append Only file(追加到日志记录文件)
Redis是一个高性能的 key-value 对存储系统。它支持存储的value类型很多,包括string(字符串)、list(链表)、hash(哈希)、set(无序集合)、zset(有序集合)
支持两种数据持久化方式:Snapshotting(快照)和 Append Only file(追加到日志记录文件)
关于并发
如果你的所有操作都是靠redis本身的机制保持原子性 不要锁也可以
如果存在读数据,写redis要保证这个数据在并发的时候是一致的。
New-Bull 并发编程由浅入深
### 基本概念
* 并发与并行
* 讨论: 写对并发代码的挑战
1. 设计好共享状态
2. 状态设计. 以Servlet 为例, servlet 是共享的. “Servlet 要无状态
3. 时序的问题
4. 优化为了达到线程安全所花费的开销,主要是锁
什么是线程安全的?
一个模块是线程安全,多线程调它的接 ,结果逻辑正确/符合预期.
要求调 者需要加锁、需要某种时序的保证
坑: RMW. Read-Modify-Write
典型的坑:
case 1: ConcurrentModificationException
fail fast : ArrayList.checkForComodification()
https://coolshell.cn/articles/9606.html rehash 导致hashmap死循环
内存缓存 deepCopy()
为什么要有并发? 利用多核, 设计上贴近现实世界。
---
### 几个核心概念
* 原子操作 : 并发场景的基本需求, 也是互斥、同步 实现的前提
* 互斥 : 并发场景的基本需求
* 同步 : 并发实体之间的通讯
* 可见性 : 解决底层并发性能优化带来的副作用
原子操作; boolean Unsafe.compareAndSwap /Unsafe.objectFieldOffset(AtomicInteger.field(“value”))
常见的互斥机制 ? 锁 信号量 Synchronized
同步 Object.wait Object.notify
Lock.condition
notify/signal 还是 notifyAll/signalAll
避免liveness VS “惊群效应” (thundering herd)
可见性 — Java
内存屏障
同步 : 并发实体之间的通讯
可 性 : 解决底层并发性能优化带来的副作
### 原子操作
* CAS, CompareAndSwap
* Java 原子操作的支持
---
### 互斥
* 常见的互斥机制
* Java 互斥机制的相关实现
---
### 同步
* Object .wait() .notify()
* Lock.condition
* Future
* 一些Helper类: Semaphore, CountDownLatch 等
---
### 可见性
* 可见性问题存在的原因
* volatile. 内存屏障. Java 内存模型
---
### Concurrent Collection
---
### Java 线程池实现 ThreadPoolExecutor
* 运行机制剖析
* 使用原则
为了线程复用以及控制资源开销
各个参数意义:
当execute(task) 时:
如果当前线程数 < corePoolSize, 则创建新线程. ( 即使当前线程中有idle的 /!\ )
如过当前线程数 >= corePoolSize, 则进队列. ( idle 的线程会从队列取task )
如果队列满了且当前线程数 < maxPoolSize, 则新建线程来处理
否则 调用 RejectedExecutionHandler 进行处理
keepAliveTime
> coreSize < maxSize 的线程在ilde时间超过这个阈值时会被销毁
pool.allowCoreThreadTimeOut(true) 可使得 <= coreSize 的线程也参照这个keepActiveTime
队列
队列的类型用于在 资源控制 和 吞吐之间做权衡
不限制capacity 的 LinkedBlockingQueue, 则maxSize无效. 吞吐受限于coreSize, 也减少了线程的创建及context switch 等开销.
SynchronousQueue. 如果有当前thread有idle的则可 "进队列" (特殊的长度为0的队列), 否则创建新的线程. 保证了吞吐
ArrayBlockingQueue, 上两种队列的一种平衡
---
### ForkjoinPool 框架及 ParallelStream
Map & Reduce = Divide & Conquer = Fork & Join
---
### 其它
* ThreadLocal
继续Tutor-team 业务整理
July 29
七天七语言
闭包是一个函数/方法
你可以向传递对象那样传递它, 以后调用
当这个函数创建的时候, 它记住了该环境下所有的变量的值. 被调用时, 它可以一直访问这些变量, 甚至这些变量已处于环境的外部.
语言:
主要是看是不是在运行期才会报类型不一致错误
静态类型
动态类型
鸭子类型
behave as a
July 31
服务整理
Aug 1
学习redis相关用法
用redisClinet.callByRead/callByWrite,免得自己来释放
Aug 2
新牛学习 -- 设计数据密集型应用
继续tutor-team服务整理,无坑
Aug 3
导出讨论区日志
sql limit offset 第一次使用
第一次申请redis
Aug 4
学习设计数据密集型应用
Aug 6
创建Job 坑
Aug 7 && Aug 8
反思
学习DDIA第七章
Aug 9
学习DDIA
## 讨论大纲
### 可靠性、可扩展性、可维护性
* 基本概念
* 可靠性
系统在困境(adversity)(硬件故障、软件故障、人为错误)中仍可正常工作(正确完成功能,并能达到期望的性能水准)
* 提高可靠性的常用方法
从三个方面去考虑即可。
* 是否一直以最高可靠性为开发目标?
在某些情况下,我们可能会选择牺牲可靠性来降低开发成本(例如为未经证实的市场开发产品原型)或运营成本(例如利润率极低的服务),但我们偷工减料时,应该清楚意识到自己在做什么。
* 可扩展性
可扩展性(Scalability)是用来描述系统应对负载增长能力的术语
* Twitter 案例讨论
* 如何描述负载、性能
首先要能简要描述系统的当前负载。负载可以用一些称为负载参数(load parameters)的数字来描述。参数的最佳选择取决于系统架构,它可能是每秒向Web服务器发出的请求、数据库中的读写比率、聊天室中同时活跃的用户数量、缓存命中率或其他东西。除此之外,也许平均情况对你很重要,也许你的瓶颈是少数极端场景
一旦系统的负载被描述好,就可以研究当负载增加会发生什么。我们可以从两种角度来看:
增加负载参数并保持系统资源(CPU、内存、网络带宽等)不变时,系统性能将受到什么影响?
增加负载参数并希望保持性能不变时,需要增加多少系统资源?
* 响应时间avg min max mean p99
* 了解SLI、SLO、SLA http://www.yunweipai.com/archives/10703.html
* 应对负载的方法
人们经常讨论纵向扩展(scaling up)(垂直扩展(vertical scaling),转向更强大的机器)和横向扩展(scaling out)(水平扩展(horizontal scaling)
大规模的系统架构通常是应用特定的—— 没有一招鲜吃遍天的通用可扩展架构(不正式的叫法:万金油(magic scaling sauce) )。应用的问题可能是读取量、写入量、要存储的数据量、数据的复杂度、响应时间要求、访问模式或者所有问题的大杂烩。
### 数据模型与查询语言
* 基本概念:关系模型、文档模型、图数据模型、NoSQL
* 文档模型
* linkedIn 案例讨论
* 文档模型只能保存单一的文档,不能处理相对关系?no
* 文档模型是完全 schemaless 吗? no
文档数据库有时称为无模式(schemaless),但这具有误导性,因为读取数据的代码通常假定某种结构——即存在隐式模式,但不由数据库强制执行【20】。一个更精确的术语是读时模式(schema-on-read)(数据的结构是隐含的,只有在数据被读取时才被解释),相应的是写时模式(schema-on-write)(传统的关系数据库方法中,模式明确,且数据库确保所有的数据都符合其模式)
读时模式类似于编程语言中的动态(运行时)类型检查,而写时模式类似于静态(编译时)类型检查。就像静态和动态类型检查的相对优点具有很大的争议性一样【22】,数据库中模式的强制性是一个具有争议的话题,一般来说没有正确或错误的答案。
读时模式更具优势,当由于某种原因(例如,数据是异构的)集合中的项目并不都具有相同的结构时。例如,因为:
存在许多不同类型的对象,将每种类型的对象放在自己的表中是不现实的。
数据的结构由外部系统决定。你无法控制外部系统且它随时可能变化。
* 文档模型能做的关系模型也能做?那文档模型的优势是什么?
JSON表示中,所有相关信息都在同一个地方,一个查询就足够了。
但是,在表示多对一和多对多的关系时,关系数据库和文档数据库并没有根本的不同:在这两种情况下,相关项目都被一个唯一的标识符引用,这个标识符在关系模型中被称为外键,在文档模型中称为文档引用【9】。该标识符在读取时通过连接或后续查询来解析。迄今为止,文档数据库没有走CODASYL的老路。
很难说在一般情况下哪个数据模型让应用程序代码更简单;它取决于数据项之间存在的关系种类。对于高度相联的数据,选用文档模型是糟糕的,选用关系模型是可接受的,而选用图形模型(参见“图数据模型”)是最自然的。
* 数据查询语言
* 声明式 VS 命令式
SQL是一种声明式查询语言,而IMS和CODASYL使用命令式代码来查询数据库
许多常用的编程语言是命令式的。例如,给定一个动物物种的列表,返回列表中的鲨鱼可以这样写
* MapReduce查询
MapReduce既不是一个声明式的查询语言,也不是一个完全命令式的查询API,而是处于两者之间:查询的逻辑用代码片断来表示,这些代码片段会被处理框架重复性调用。它基于map(也称为collect)和reduce(也称为fold或inject)函数,两个函数存在于许多函数式编程语言中。
文档数据库的应用场景是:数据通常是自我包含的,而且文档之间的关系非常稀少
图形数据库用于相反的场景:任意事物都可能与任何事物相关联。
### 存储与检索
* 驱动数据库的数据结构
* hash索引
* 追加日志、分段压缩、删除记录
* 追加日志 VS 更新日志
关于恢复数据:
如果数据库重新启动,则内存散列映射将丢失。原则上,您可以通过从头到尾读取整个段文件并在每次按键时注意每个键的最近值的偏移量来恢复每个段的哈希映射。但是,如果段文件很大,这可能需要很长时间,这将使服务器重新启动痛苦。 Bitcask通过存储加速恢复磁盘上每个段的哈希映射的快照,可以更快地加载到内存中。数据库可能随时崩溃,包括将记录附加到日志中途。 Bitcask文件包含校验和,允许检测和忽略日志的这些损坏部分。
由于写操作是以严格顺序的顺序附加到日志中的,所以常见的实现选择是只有一个写入器线程。数据文件段是附加的,否则是不可变的,所以它们可以被多个线程同时读取。
追加 VS 更新:
追加和分段合并是顺序写入操作,通常比随机写入快得多,尤其是在磁盘旋转硬盘上。在某种程度上,顺序写入在基于闪存的固态硬盘(SSD)上也是优选的【4】。我们将在第83页的“比较B-树和LSM-树”中进一步讨论这个问题。
如果段文件是附加的或不可变的,并发和崩溃恢复就简单多了。例如,您不必担心在覆盖值时发生崩溃的情况,而将包含旧值和新值的一部分的文件保留在一起。
合并旧段可以避免数据文件随着时间的推移而分散的问题。
缺陷:
散列表必须能放进内存
如果你有非常多的键,那真是倒霉。原则上可以在磁盘上保留一个哈希映射,不幸的是磁盘哈希映射很难表现优秀。它需要大量的随机访问I/O,当它变满时增长是很昂贵的,并且散列冲突需要很多的逻辑【5】。
范围查询效率不高。例如,您无法轻松扫描kitty00000和kitty99999之间的所有键——您必须在散列映射中单独查找每个键。
* SSTables和LSM树
我们要求键值对的序列按键排序。乍一看,这个要求似乎打破了我们使用顺序写入的能力,但是我们马上就会明白这一点。我们把这个格式称为排序字符串表(Sorted String Table),简称SSTable。求每个键只在每个合并的段文件中出现一次(压缩过程已经保证)
* 合并和压缩的过程
合并段是简单而高效的,即使文件大于可用内存。这种方法就像归并排序算法中使用的方法一样,如图3-4所示:您开始并排读取输入文件,查看每个文件中的第一个键,复制最低键(根据排序顺序)到输出文件,并重复。这产生一个新的合并段文件,也按键排序。
如果在几个输入段中出现相同的键,该怎么办?请记住,每个段都包含在一段时间内写入数据库的所有值。这意味着一个输入段中的所有值必须比另一个段中的所有值更新(假设我们总是合并相邻的段)。当多个段包含相同的键时,我们可以保留最近段的值,并丢弃旧段中的值
为了在文件中找到一个特定的键,你不再需要保存内存中所有键的索引。以图3-5为例:假设你正在内存中寻找键 handiwork,但是你不知道段文件中该关键字的确切偏移量。然而,你知道 handbag 和 handsome 的偏移,而且由于排序特性,你知道 handiwork 必须出现在这两者之间。这意味着您可以跳到 handbag 的偏移位置并从那里扫描,直到您找到 handiwork(或没找到,如果该文件中没有该键)。
您仍然需要一个内存中索引来告诉您一些键的偏移量,但它可能很稀疏:每几千字节的段文件就有一个键就足够了,因为几千字节可以很快被扫描i。
写入时,将其添加到内存中的平衡树数据结构(例如,红黑树)。这个内存树有时被称为内存表(memtable)。
当内存表大于某个阈值(通常为几兆字节)时,将其作为SSTable文件写入磁盘。这可以高效地完成,因为树已经维护了按键排序的键值对。新的SSTable文件成为数据库的最新部分。当SSTable被写入磁盘时,写入可以继续到一个新的内存表实例。
为了提供读取请求,首先尝试在内存表中找到关键字,然后在最近的磁盘段中,然后在下一个较旧的段中找到该关键字。
有时会在后台运行合并和压缩过程以组合段文件并丢弃覆盖或删除的值。
* 如何崩溃恢复
这个方案效果很好。它只会遇到一个问题:如果数据库崩溃,则最近的写入(在内存表中,但尚未写入磁盘)将丢失。为了避免这个问题,我们可以在磁盘上保存一个单独的日志,每个写入都会立即被附加到磁盘上,就像在前一节中一样。该日志不是按排序顺序,但这并不重要,因为它的唯一目的是在崩溃后恢复内存表。每当内存表写出到SSTable时,相应的日志都可以被丢弃。
* B 树
* 为什么具有 n 个键的B树总是具有 O(log n) 的深度
* B 树的存储和查询过程
* WAL 是什么?为什么需要WAL
为了使数据库对崩溃具有韧性,B树实现通常会带有一个额外的磁盘数据结构:预写式日志(WAL, write-ahead-log)(也称为重做日志(redo log))。这是一个仅追加的文件,每个B树修改都可以应用到树本身的页面上。当数据库在崩溃后恢复时,这个日志被用来使B树恢复到一致的状态【5,20】。
* 比较B树和LSM树
LSM树通常能够比B树支持更高的写入吞吐量,部分原因是它们有时具有较低的写放大(尽管这取决于存储引擎配置和工作负载),部分是因为它们顺序地写入紧凑的SSTable文件而不是必须覆盖树中的几个页面【26】。这种差异在磁性硬盘驱动器上尤其重要,顺序写入比随机写入快得多。
LSM树可以被压缩得更好,因此经常比B树在磁盘上产生更小的文件。 B树存储引擎会由于分割而留下一些未使用的磁盘空间:当页面被拆分或某行不能放入现有页面时,页面中的某些空间仍未被使用。由于LSM树不是面向页面的,并且定期重写SSTables以去除碎片,所以它们具有较低的存储开销,特别是当使用平坦压缩时【
### Redis
* 数据库
* 用到了我们上面讨论的哪些技术?数据模型、查询语言、存储、检索
* Redis的过期键删除策略
* 为什么说redis是单线程的?服务端单线程怎么和客户端线程池怎么配合?
}}
{{{
## 讨论大纲
### 基本概念
* ACID 的含义
* 单对象和多对象操作
### 隔离级别
#### 弱隔离级别
* 读已提交(Read Committed)
* 快照隔离和可重复读
* 防止丢失更新
* 写入偏差与幻读
#### 可序列化
* 串行执行
* 两阶段锁定(2PL)
* 序列化快照隔离(SSI Serializable Snapshot Isolation)
### Redis 事务处理
* Redis 事务的实现
* Redis Lua 脚本的执行是否是原子的?
* Redis 对 ACID 的支持
### MySQL 事务处理
* 隔离级别
* 几种隔离级别
* 为什么 RR 为默认隔离级别
* MySQL的 RR 完美的解决了幻读问题吗?如果解决了幻读,还需要SERIALIZABLE?
* 锁
* 锁的类型
* CRUD 加什么锁
* 每种隔离级别加什么锁
* 死锁
* MVCC
* MySQL 事务的实现
Aug 10
处理业务
Aug 12
分布式系统学习
### CS 分布式系统带来的优势
* scalability
* performance
* availability
* performance
---
### CS 分布式系统带来的问题
* number of nodes
* distance between nodes
* 可理解性
---
### 为了处理可理解性引入的Abstraction 和 Model
---
### 分布式系统中我们拥有的武器
* 思想:divide and conquer
* Partition
* Replication
---
### 分布式系统抽象与建模详解
* System Model
* node
* communication link
* over-all assumption
* guarantee
* Case Study: Consensus Problem, and impossible results
---
### 常见的Partition技术
* Partition Of Key-Value Data
* Partition Of Secondary Indexes
* Rebalancing Partitions
* Request Routing
---
### 常见的Replication技术
* Single Leader
* Multi Leader
* Leaderless
* Problems of Replication Lag
---
---
Aug 13-14
业务-更新lesson Job
上线业务
Aug 15
Aug 16
学习一致性和共识算法
* Linearizability定义
* informal 定义
* 读不可flash back
* 使用Linearizability保证正确性
* Locking和选主
* 保证约束与唯一性
* Cross-Channel方式对timing有依赖
* 实现Linearizable系统
* 常见复制方式是否能实现linearizable system
* 解析最难理解场景, leaderless replication下的Quorum不能实现Linearizable原因
* Linearizability的代价
* 先批评一下CAP
* Linearizability 与 SystemModel.fault.networkPartition
* Linearizability 与 SystemModel.communicationLink.networkDelay
### systemModel.guarantee.ordering
* Ordering 与 Causality
* 场景概述Causality
* Causal Order是偏序
* 比较Linearizable Consistency与 Causal Consistency
* 捕捉 Causality
* Sequence Number Ordering
* Lamport timestamps 与其局限性(Uniqueness Problem)
* Total Order Broadcast
* 定义
* Linearizability 与 Total-Order-Broadcast
### 分布式事务与Consensus
* Atomic Commit 与 2PC
* 实现
* 问题
* 变种3PC
* 实践中的分布式事务
* Exactly-once 消息处理
* XA
* Consensus
* 定义问题
* Consensus与total-order-broadcast的关系
* Consensus与single-leader replication的关系
* Consensus的局限性
* 存活检测 与 协调服务
Aug 17
去除代码依赖
Aug 19-20
新牛 消息队列
## 讨论大纲
### 基本概念
* 消息中间件/消息队列
* 松耦合设计、事件驱动架构、流式处理
* 推模式/拉模式
* 吞吐量、负载均衡、持久化、可扩展
### 应用场景
* 场景
* 消息没有收到
* 我想消息7天内有效,超时清除
* 我想业务处理完毕之后,在回ack
* 我想发送一个45分钟后投递的消息
* 我的消息没人消费了
* ...
* 异步数据交互
* 行为跟踪
* 日志收集
* 流处理
* 主流消息中间件
* activeMQ
* rabbitMQ
* kafka
* rocketMQ
* 消息中间件作用
* 解耦
* 持久化
* 削峰
* 缓冲
* 可恢复
* 可扩展
* 异步通信
* 顺序性
### producer
#### 序列化
* protobuf
* json
* thrift
* avro
* 自定义
#### 发布消息
* 路由
* 消息提交
* 同步发送
* 异步发送
* 批量发送
* 顺序性
* 超时机制
* 重试机制
### broker
* 选举
* 脑裂
* 复制
* 物理存储
* 文件
* 索引
* 顺序写
* 随机读
* 消息队列
* 内存
* 磁盘
* 惰性队列
### consumer
* 消费组
* 轮询/长轮询
* offset
* 自动提交
* 同步提交
* 异步提交
* 幂等
* 组合提交
* 再均衡
* 反序列化
* 消息去重
Aug 21
tutor-team 代码迁移上线
Aug 22
上线最后一次业务
实习总结