mit6.824-lab2b日志一致性
目录
- Start函数
- AppendEntries函数
- 整体架构&goroutine
主要内容:client调用start()添加日志后,raft如何保证日志如何被安全、可靠、快速的添加到raft集群中。
Start函数
客户端调用Start函数后应立即返回,无论raft添加结果如何。如果当前节点是leader的话,则更新自己的日志。
//客户端发送command消息
func (rf *Raft) Start(command interface{}) (int, int, bool)
...
//leader: 更新日志
log := Log{term, command, index}
rf.entries = append(rf.entries, log)
rf.matchIndex[rf.me] = len(rf.entries) - 1
rf.cond.Signal() //通知发送更改包(心跳)
AppendEntries函数
leader通过调用AppendEntries rpc来更新follower的日志。
leader由nextIndex[i]可以知道要发送给该服务器的下一条日志索引,并将前一条日志的索引(prevLogIndex)、任期(prevLogTerm)、Entries[nextIndex[i]:]作为参数发送给follower,follower凭借上列参数更新自己的日志。
接收者(follower)具体流程:
- 检测leader合法性
- 重置选举计时器
- 检测日志中是否包含与leader一致的Entries[prevLogIndex]
- 根据参数Entries更新自己的日志。
- 更新commitIndex
过程很简单,可是raft论文中有很多隐晦的地方没有讲清楚,算是有不少坑在这里。
快重传:在前三步的检测失败时,告知leader失败的原因,并且告知leader下一次发送的nextIndex[i]。
在reply中加入三个变量:Xterm(冲突的任期)、Xlen(日志空位)、Xindex(下次发送的index)。
如果follower存在prevLogIndex的日志,并且发生冲突的话,返回follower冲突日志所对应任期的第一条日志Index。
https://mit-public-courses-cn-translatio.gitbook.io/mit6-824/lecture-07-raft2/7.3-hui-fu-jia-su-backup-acceleration
第四步中,需要注意:添加follower中没有的日志,需要处理已经包含、冲突、缺少的问题
如果leader的nextIndex=[2,2,2],follower的日志在更新完后必须为1234。
server | 当前日志 | 接收的日志 |
---|---|---|
leader | 1234 | |
follower1 | 123 | [3,4] |
follower2 | 1256 | [3,4] |
follower3 | 12 | [3,4] |
整体架构&goroutine
go rf.ticker()
go rf.UpdateOtherLogsLoop()
go rf.applier()
go rf.UpdateOtherLogSignal()
ticker:lab2a选举相关
UpdateOtherLogs*:发送心跳包,发送心跳的时候顺带更新日志
applier:周期检查日志是否被提交,如果已经提交,则应用到状态机,<-Applych
发送心跳包用了两个goroutine:Loop和Signal。
Loop为周期性发送心跳,每隔100ms发送一次。
Signal为响应式发送心跳,包阻塞在sync.cond上,每当Client调用Start()时,leader成功添加command到自己的日志,就会调用cond.signal(),立即发送更新日志的请求。
如果只用Loop发送心跳包,运行lab2b:90s+,Loop+Signal:60s+。