JRaft
JRaft
是蚂蚁金服开源的一个Raft
协议的实现
新节点如何追上旧节点
官方wiki说明
当一个 raft 节点重启的时候,内存中的状态机的状态将会丢失,在启动过程中将重放日志存储中的所有日志,重建整个状态机实例。这就导致两个问题:
- 如果任务提交比较频繁,比如消息中间件这个场景,那么会导致整个重建过程很长,启动缓慢。
- 如果日志很多,节点需要存储所有的日志,这对存储是一个资源占用,不可持续。
- 如果增加一个节点,新节点需要从 leader 获取所有的日志重放到状态机,这对 leader 和网络带宽都是不小的负担。
因此,通过引入 snapshot 机制来解决这 3 个问题,所谓 snapshot 就是为当前状态机的最新状态打一个”镜像“单独保存,在保存成功后,在这个时刻之前的日志就可以删除,减少了日志存储占用;启动的时候,可以直接加载最新的 snapshot 镜像,然后重放在此之后的日志即可,如果 snapshot 间隔合理,那么整个重放过程会比较快,加快了启动过程。最后,新节点的加入,可以先从 leader 拷贝最新的 snapshot 安装到本地状态机,然后只要拷贝后续的日志即可,可以快速跟上整个 raft group 的进度。
代码浅析
Leader节点发送快照
代码实现
1 | private boolean sendEntries(final long nextSendingIndex) { |
装填请求参数信息
1 | private boolean fillCommonFields(final AppendEntriesRequest.Builder rb, long prevLogIndex, final boolean isHeartbeat) { |
接着就是Leader
节点将发起AppendEntries RPC
请求了
Follower节点接收快照
NodeImpl
重要实现方法——InstallSnapshotRequestProcessor
1 |
|
下载快照文件
1 |
|
加载下载好的 snapshot 文件
1 | void loadDownloadingSnapshot(final DownloadingSnapshot ds, final SnapshotMeta meta) { |
发布、接收快照安装事件
1 |
|
应用到状态机处理 snapshot load task 任务
1 | private long runApplyTask(final ApplyTask task, long maxCommittedIndex, final boolean endOfBatch) { |
执行 snapshot load 操作
1 | private void doSnapshotLoad(final LoadSnapshotClosure done) { |
snapshot load操作完成后
1 | private void onSnapshotLoadDone(final Status st) { |
实现日志回放操作(如果接受快照的节点,存在日志缺省的情况,通过日志回放操作补全 logEntry)
1 | private void doCommitted(final long committedIndex) { |