Raft协议详解

张开发
2026/4/17 20:31:55 15 分钟阅读

分享文章

Raft协议详解
一、Raft协议基本流程1、示例动画先来看下Raft协议到底是个什么样子的。网上有个动画文稿是对Raft算法最生动形象的描述。地址 http://thesecretlivesofdata.com/raft/从这个动画中可以看到 Raft协议是分为两个阶段工作的Election选举和Log Replicaion日志同步。也就是说Raft要解决的其实是两个事情一个是集群中选举产生主节点。另一个是在集群内进行数据同步。2、工作流程Raft算法的基本工作流程是这样的这里重点是需要理解Log日志和State Machine状态机。Log日志就是保存在Server上的操作日志其中每个条目称为Entry。Entry中的操作最终都会落地到StateMachine中。Raft算法的核心就是要保证所有节点上的Entry顺序一致。注意是保证Entry顺序一致而不是保证Entry不丢失这是两个概念。1、多个Server基于他的一致性协议会共同选举产生一个 Leader负责响应客户端的请求。2、Leader 通过一致性协议将客户端的指令转发到集群所有节点上。3、每个节点将客户端的指令以 Entry 的形式保存到自己的 Log 日志当中。此时 Entry 是uncommited状态。4、当有多数节点共同保存了 Entry 后就可以将 Entry 中的客户端执行提交到State Machine 状态机中。此时 Entry 更新为commited状态。3、节点的三种状态为此Raft协议给每个节点设定了三种不同的状态LeaderFollower和Candidate。Leader 1、选举产⽣。多数派决定。2、向 Follower 节点发送⼼跳Follower 收到⼼跳就不会竞选Leader。3、响应客户端请求。集群内所有的数据变化都从 Leader 开始。4、向 Follower 同步操作⽇志。 具体实现时有的产品会让发到 Follower 上的请求转发到 Leader 上去。 也有的直接拒绝Follower1、参与选举投票。2、同步 Leader 上的数据。3、接收 Follower 的⼼跳。如果 Follower ⻓期没有发送⼼跳就转为 Candidate竞选 Leader。Candidate没有 Leader 时发起投票竞选 Leader。4、节点状态变化Raft协议为了保证同一时刻集群当中最多只会有一个主节点防止脑裂问题还会增加一个Term任期的概念。时间被划分为多个任期。每个任期都以选举开始。选举成功后由一名Leader管理集群直到任期结束。有些选举失败了没有选举出Leader那就进入下一个任期开始下一次选举。从CAP理论的角度分析Raft优先保证的是CP而放弃了A。与之形成对比的是Eureka保证AP。他们的状态变化过程是这样的1、所有节点启动时都从 Follower 状态开始。2、每个Follower 设定了⼀个选举过期时间Election Timeout 。Follower持续等待 Leader 的⼼跳请求。如果超过选举过期时间就转为 Candidate向其他节点发起投票竞选 Leader。为了防⽌所有节点在同⼀时间过期这个选举过期时间通常会设定为⼀个随机值⼀般在 150ms到 300ms之间。3、Candidate 开始新⼀个任期的选举。每个 Candidate 会投⾃⼰⼀票然后向其他节点发起投票 RPC 请求。然后等待其他节点返回投票结果。等待时⻓也是Election Timeout。4、每个节点在每⼀个任期内有⼀次投票的资格。他们会响应 Candidate 的投票 RPC 请求。按照⼀定的规则进⾏投票。返回⽀持 或者 不⽀持。5、Candidate 收到其他节点的投票 RPC 响应之后会重置他的 Election Timeout继续等待其他响应。⼀旦某⼀个 Candidate 接收到了超过集群⼀半节点的投票同意结果后就会转为 Leader 节点。并开始向其他节点发送⼼跳 RPC 请求。确认⾃⼰的 Leader 地位。6、其他节点接收到 Leader 的⼼跳后就会乖乖的转为 Follower 状态。 Candidate 也会转为 Follower 。然后等待从 Leader 同步⽇志。直到 Leader 节点⼼跳超时或者服务宕机再触发下⼀轮选举进⼊下⼀个 Term任期。二、Raft协议实现机制接下来思考下Raft算法要如何实现这些流程呢这里我们主要分析每个节点要保存哪些数据然后RPC请求要传递哪些数据。这里简单总结Raft算法的基础数据结构首先是数据所有节点都需要的信息currentTerm 服务器当前的任期votedFor当前任期内投票给了谁。log[]日志条目Entry。每个 Entry 要包含Command客户端指令term任期idxEntry 的偏移量。commitIndex标记为commited的 Entry 的索引。记录消息同步的进度。lastApplied已执行完 Command的 Entry 索引。 记录往状态机提交的进度。lastAppliedcommitIndex。 这两个主要是提交到状态机需要Leader 上的特有参数nextIndex[] : 给每个 Follower 同步到了哪一条 Entry。记录与follower 的同步进度。matchIndex[]给每个 Follower 已经复制到了哪一条 Entry。主要是要记录有哪些 Entry 发给 Follower正在等待 Follower 确认中。然后看RPC 请求最为核心的有三个。第一个是 Candidate投票的 RPC 请求。第二个是 Leader 发送的心跳请求。第三个是Leader 发送的日志同步请求。对于投票请求主要请求参数term : 当前任期candidateId 投票的候选人 ID。lastLogIndex候选人的最后日志Entry 索引。last logo term候选人最后日志条目的任期号。前两个参数是必须的。后两个参数主要是当主从发生切换时可以用来找出最新的Candidate。主要响应参数term : 当前任期号voteGranted投票结果。是否支持当前 Candidate 当选为 Leader。**对于后两个请求都是有 Leader 往 Follower 发送。**其实可以合并为一种请求。心跳请求不带日志条目而同步日志请求带日志条目。Follower 只要判断下有没有日志条目就可以区分是哪种请求。主要请求参数term当前领导者的任期leaderId当前领导者的 ID。entries[]要同步的日志条目。心跳请求就传空。同步消息请求则可以支持批量同步。leaderCommit领导者已知已提交的最高的日志条目的索引。主要是 Follower 要知道新的条目是要从哪里开始同步。为了安全起见论文中还建议将上一条 Entry 的Index 以及 Term 发送过来。主要还是用来协助 Follower 定位 Entry主要响应参数term 当前任期。success 响应是否成功。

更多文章