前置
请先看完 http://www.kailing.pub/raft/index.html 后继续查看文章
角色
- leader
- follower
- candidate
每个节点同时只扮演一个角色,允许切换
Leader
领导者,接收客户端请求,使用日志条目复制数据到Follower
维护状态
- term: 当前任期号
- log[]: 日志条目数组,每个条目包含客户端指令和产生该条目的任期号
- commitIndex: 已被集群多数节点复制且可以提交的日志最大索引(所有≤该索引的日志均可应用到状态机)
- lastApplied: 已应用到状态机的日志最大索引(记录本地状态机执行到哪条日志)
- nextIndex[]: 对每个 follower 维护一个值,表示下一次向该 follower 发送日志的起始索引
- matchIndex[]: 对每个 follower 维护一个值,表示该 follower 已成功复制的最大日志索引
Follower
跟随者,被动接受Leader的日志复制请求和心跳包
维护状态
- term: 当前任期号(与 leader 保持一致,或在发现更高任期时更新)
- log[]: 日志条目数组(与 leader 同步的日志副本)
- commitIndex: 已被集群多数节点复制且可以提交的日志最大索引(由 leader 通过心跳同步)
- lastApplied: 已应用到状态机的日志最大索引(记录本地状态机执行到哪条日志)
- votedFor: 当前任期内投票给的节点 ID(用于避免重复投票,保证每个任期仅投一票)
- 超时计时器:用于检测 leader 是否失联(若超时未收到 leader 心跳,会转换为 candidate 发起选举)
Candidate
候选者,当Follower在一定时间没有接收到Leader心跳包后将从follower切换到candidate进行选举
请求
日志复制
作用: 专门用于向 follower 同步新的日志条目
内容: 包含 leader 的任期号(term
)、前一条日志的索引和任期(用于 follower 校验匹配)、需要同步的日志条目(可能是一条或多条)、leader 当前的commitIndex
(用于同步提交状态)
心跳包
作用:用于维持leader
的地位,定时发送,防止follower
因超时触发新的选举。
内容:格式与日志复制请求基本一致,但不包含任何新的日志条目(可以理解为空日志的复制请求
),同样会携带 leader 的term
和commitIndex
投票请求
作用: 竞选leader
内容: 等待150ms-300ms随机时间过期后为自身term+1,给自己投一票,然后向其他节点发送投票请求,多数派同意后变成leader
成员变更请求
作用: 动态增删节点,避免脑裂问题
执行流程
- 客户端向Leader发送请求
- Leader接受到请求,使用日志复制将数据同步到Follower
- 多数派Follower处理完成响应给Leader
- Leader收到多数派响应,修改自身CommitIndex,返回响应给客户端
- Leader等待下次心跳包进行同步提交信息
如果在同步提交信息前挂掉了怎么办?
此时Follower变成Candidate进行选举,注意,只有日志与多数派相同的Candidate才会被选举为Leader
- 新的Leader当选,任期+1,立刻触发日志同步步骤如下
- 向所有follower发送包含自身日志信息的同步请求(基于
nextIndex
初始化值) - 当follower返回响应时,NewLeader会通过
matchIndex
记录每个follower已复制的最大日志索引 - 当NewLeader发现某条日志的
matchIndex
已满足多数派已复制(即超过半数 follower 的matchIndex
≥该日志索引),就会直接将自己的commitIndex
更新为该日志索引 - 此时之前Leader未同步提交的信息将会被标记提交
Reference
[]: http://www.kailing.pub/raft/index.html "Raft 算法动画演示"
[]: https://chatgpt.com/ "ChatGPT"
[]: https://www.doubao.com/ "豆包"