可能是你能找到的最完整最详细的中文版的Raft算法说明博客
内容来源都是原生的论文,可以保证内容的可靠性,并且对论文里面的很多细节做了扩展说明
Role
candidate、follower、leader
role不同,数据结构就不同,遵循的rule也不同
All Server Rules:
- 如果RPC里面commitIndex>appliedIndex,则更新appliedIndex。
- leader角色中,统计超过半数节点copy成功,那么commitIndex就可以增加,增加完之后,就可以apply 命令到状态机中,最后更新appliedIndex
- Follower角色中,confirm了copy之后,下一个Leader的心跳中会通知更新最新的leaderCommitIndex,然后Follower就会做出对应的调整commitIndex和appliedIndex
- 无论任何情况下,当RPC返回的term比自己的大时,不管你是什么身份,都成为follower。
你当前role是leader,你发AppendEntry RPC消息给follower,结果follower返回一个比你大的term,说明你网络不好,已经有新leader了,所以你要转变角色为follower。而新的leader可能是这个follower,也可能是你不知道的其他节点
你当前role是candidate,你发RequestVote RPC消息给follower,结果follower返回一个比你大的term,说明你这轮选举失败了,已经有比你更新的leader了,所以你要转变角色为follower
Followers Rules:
- Respond to RPCs from candidates and leaders(收到来自leader或者candidate的RPC消息,要响应)
- If election timeout elapses without receiving AppendEntries RPC from current leader or granting vote to candidate:convert to candidate(在一个心跳超时周期内,如果没收到任何消息,则身份成为candidate)
Candidates Rules:
On conversion to candidate, start election:成为candidate要做的事情
• Increment currentTerm
• Vote for self
• Reset election timer
• Send RequestVote RPCs to all other servers
注意第二条描述,candidate只有监听到newLeader的消息才会服从成为follower,加入老leader抽风了一下,被follower发觉,follower发起candidate后但是未成为new leader时,又收到了老leader的心跳,是不服从,直接拒绝的
• If votes received from majority of servers: become leader
• If AppendEntries RPC received from new leader: convert to follower
• If election timeout elapses: start new election
Leader Rules:
一旦成为leader,第一时间会发一个特殊的心跳给所有的节点,申明地位,阻止小弟们继续选举,并且会发送一个内容为空的log。
leader要定时发送消息给小弟们,即使没有写操作,也要发一个空的心跳消息给小弟们
leader收到客户端的写的操作是,第一时间是自己持久化这个消息,然后把消息RPC给所有follower,超过半数Node添加好了这个消息的时候,就把这个消息的状态标志为applied,并且反馈client操作成功,最后在下一个心跳消息中通知所有的follower 可以applied这个消息了
leader发AppendRPC给follower,如果有的小弟因为日志的序号不对,说明小弟的消息和自己的不一致,有缺失,则更新leader的nextIndex[小弟Index]的值为递减值,重试消息,直到消息完全一致
一旦选举成功:发一个初始化空日志的AE RPC,并且持续保持心跳
• If command received from client: append entry to local log,respond after entry applied to state machine (§5.3)
• If last log index ≥ nextIndex for a follower: sendAppendEntries RPC with log entries starting at nextIndex
• If successful: update nextIndex and matchIndex forfollower (§5.3)
• If AppendEntries fails because of log inconsistency:decrement nextIndex and retry (§5.3)
• If there exists an N such that N > commitIndex, a majority of matchIndex[i] ≥ N, and log[N].term == currentTerm:set commitIndex = N (§5.3, §5.4)
这里解决的就是前leader遗留给新leader且未commit的命令
比如 S1有 MS1 MS2 MS3 MS4 MS5命令,commitIndex为3,还有MS4 MS5命令未commit,而且并不知道其它Node是否commit了,所以会发一个空命令的 MS6申明地位,这时候,N=6 >commitIndex=3,最终大家都commit了MS6,那么commitIndex=6
通用Rules
1个term里,最多只会有一个leader,如果老leader挂了,新leader在这个term选举又失败了,那么可能一个term里没有leader,但是绝对不会出现2个leader
leader-append-only,确保了leader的权威,所有的节点的数据都要保持和leader一样,即使你的log比leader要多,也要服从leader。而且leader不仅不会覆盖自己的log,也不会删除自己的log,所以老leader遗留给新leader的所有的命令都会被commit,不会糟践
日志比对:如果log Index和log Term一样,那么所有的这样的log的内容是一样的