前言
本文的局限性在于,本文不完全按照论文(raft extended)实现,并以通过所有测试和自我满足为目的。在部分与论文不一致,或论文没有详细讨论的地方,我将以引用的格式来说明。
文本并不详细论证Raft,即不按照领导选取(leader election)、日志复制(log replication)、安全(safety)展开,并且(暂时-2020/10/4)不讨论成员变化(membership changes)。本文将把这些部分融合起来,全面展示各个部分的状态变化。
在实现Raft的时候,加锁是个难题,鄙人亦有所总结,才疏学浅,望不吝赐教。文本将不讨论所有加锁的细节。
所有持久化的状态
- currentTerm 当前的Term id
- votedFor 当前任期我投票给谁了
- log[] 日志的顺序列表;每个日志包括了给状态机执行的命令,所在term id
非持久化状态
- commitIndex 已收到的日志
- lastApplied 已投喂给状态机的日志
Leader非持久化的状态
- nextIndex 对于Leader来说,每个Follower的下一个提交位置
- isSynchronizing 是否正在和某个Follower同步信息,一般当Follower日志对不上时启动一下
这里并没有使用matchIndex。因为我不理解这nextIndex和matchIndex的区别:matchIndex+1不就是nextIndex吗?
isSynchronizing 是用来同步大量数据的时候,标记一下Leader正在和谁同步大量数据,避免重复通信大量数据。一般的心跳包和日志传输用不到。这个是自己实现时考虑到而加上的。
Raft在运行过程中,主要由这三个死循环来驱动:检查心跳并发动选举、发送日志、投喂状态机。
持久化并不是难点,找到每个修改持久化状态的地方,保存一下就好。所以这文篇章也不会讨论持久化。
检查心跳并发动选举
跟随者 Follower
Follower的所有行为都依靠以下三件事来驱动:
- 收到了AppendEntries(被动)
- 收到了RequestVote(被动)
- 心跳超时(主动)
两个被动触发的事件相互相对独立,但是都会影响到心跳超时。
收到了AppendEntries
如果收到了AppendEntries,则:
- 检查args.Term,如果args.Term比currentTerm,更新currentTerm,如果Term更低,返回false
- 将自己的commitIndex添加到reply之中
- 刷新心跳倒计时,并把自己设为Follower
- 判断是否接受日志条目
- 将commitIndex添加到reply之中,是我自作主张的优化。当出现一个有一大段记录与Leader不同的Followerÿ