基本概念
所有节点的持久数据:
- currentTerm:当前节点视角的任期,初始化0
- votedFor:当前任期给谁投的票
- log[]:log entries,由term和command组成,第一个索引为1
所有节点的易变数据:
- commitIndex:已知提交的最高日志项的索引
- lastApplied:应用于状态机的最高日志项的索引
Leader的易变数据:
-
nextIndex[]:要发送到每个follwer的下一个日志条目的索引(初始化为leader最后一个日志索引+ 1)
-
matchIndex[]:对于每个服务器,已知要在服务器上复制的最高日志条目的索引(初始化为0,单调递增)
Term
任期,整数类型,初始0,raft中的时间线,用于区分过期消息。
规则:
-
每次通信(选举、log复制)都携带Tern
-
Term自动更新机制,当发现接收请求的Term更大:
- Follower:直接更新
- Leader:结束当前任期,变为Follower并更新Term
- Candidate:结束当前选举,变为Follower并更新Term
Rules for Servers
All Servers
- 如果commitIndex > lastApplied: 增加lastApplied,则向状态机应用日志[lastApplied]
- 如果RPC请求或响应包含term T > currentTerm:设置currentTerm = T,转换为follower
Followers
- 回应候选人和领导的竞选请求
- 如果超过election timeout没有收到AppendEntries或RequestVote:转换为候选人
Candidates
-
转换为候选人后,开始选举:
- 增加currentTerm
- 为自己投票
- 重置选举计时器
-
发送RequestVote rpc到所有其他服务器
-
如果获得大多数服务器的投票:成为领导者
-
如果从新的leader接收到AppendEntries RPC:转换为follower
-
如果选举超时:开始新的选举
Leaders
-
结束选举时:发送初始空的AppendEntries rpc (心跳)到每个服务器;在空闲期间重复以防止选举超时。
-
如果从客户端收到命令:追加条目到本地日志,在条目应用到状态机后响应
-
如果最大 index ≥ nextIndex 发送 AppendEntries RPC 带有 entries starting at nextIndex
- 如果成功:更新追随者的nextIndex和matchIndex
- 如果AppendEntries因为日志不一致而失败:递减nextIndex并重试
-
如果存在N,使得N > commitIndex