今天来学习分布式一致性-Raft

235 阅读5分钟

出于对性能或者稳定性的考虑,我们现在应用与中间件大都是分布式架构部署的,分布式集群部署一定会遇到的就是多台机器间的数据一致性问题,raft是最经典的分布式一致性算法之一,在本文,我们就聊一聊raft算法。

1. Raft的核心链路

在Raft共识算法中,有三个核心阶段:领导者选举(Leader Election)日志复制(Log Replication)日志应用(Log Application) 。每个阶段在算法中发挥不同的作用,以确保分布式系统中的一致性和可靠性。

2. 选主

领导者选举是Raft算法的第一个核心阶段,其目的是在分布式系统中选择一个领导者(Leader)。

Raft 集群中有三种角色:

  • Leader: 所有请求的处理者,接收客户端发起的操作请求,写入本地日志后同步至集群其它节点。
  • Follower: 请求的被动更新者,从 leader 接收更新请求,写入本地文件。如果客户端的操作请求发送给了 follower,会首先由 follower 重定向给 leader。
  • Candidate: 如果 follower 在一定时间内没有收到 leader 的心跳,则判断 leader 可能已经故障,此时启动 leader election 过程,本节点切换为 candidate 直到选主结束。

这其中最重要的就是主节点Leader,所有节点的数据都是向Leader看齐的,就像主从集群中的主节点,所有操作都是先写入主节点,再由主节点同步给从节点。

在Raft中,所有从节点会以心跳的形式感应主节点的存在,一旦从节点在超时时间内感应不到主节点的存在,就会发起选举过程。

对于心跳超时时间的选择,raft也有自己的设计,如果所有节点在同一时刻启动,经过同样的超时时间后同时发起选举,那么很有可能陷入“活锁”状态,所有节点都争抢成为leader(后面我们会详解这一过程)。为了避免这一情况,Raft 使用随定时器,让每个节点的“超时时间”在一定范围内随机生成,这样就大大的降低了多个节点同时发起选举的可能性。

2.1. 一次选主的全过程

与Leader心跳断连后,就会进行选主的过程:

  1. 原Leader下线,心跳断连
  2. Follower1请求升级为Leader
  3. 超过半数的Follower同意其成为Leader
  4. Follower1成为新Leader
  5. 老Leader上线后成为Follower

image.png

image.png

image.png

image.png

image.png

image.png 以上是一次理想的选举过程,但是,现实不会如此顺利。通常情况下,往往是多个Follower同时“竞争上岗”,这也是我们需要着重关注的选主场景:

2.2. term

首先,我们需要介绍raft中的一个重要概念term。term是一个递增的逻辑时钟。

逻辑时钟(Logical Clock)是一种用于分布式系统中的机制,用来为事件提供顺序性,解决在分布式环境中事件排序和一致性问题。逻辑时钟不依赖于物理时间,而是通过逻辑的方式来标记和比较事件的顺序。

当 candidate 触发选举时会增加 term,如果一个 candidate 赢得选举,他将在本 term 中担任 leader 的角色。但并不是每个 term 都一定对应一个 leader,有时候某个 term 内会由于选举超时导致选不出 leader,这时 candicate 会递增 term 号并开始新一轮选举。

candidate发出选举请求时,会携带自己的term+index,当follower收到的选主请求中的term比本身的term大时,follower会同意此次选主请求,反之则拒绝。

除term外,follower也会比较index,index是commitlog的索引,index越大,代表节点的数据越新,只有请求的日志比自己的新,follower才能认可其成为leader。

image.png

如果一次选主请求没能获得半数以上节点的同意,那么此次选主竞选就是失败的,candidate需要将term+1,重新开始竞选。

3. 日志复制

raft算法基于状态复制机Replicated State Machine)模型,所有节点从同一个 state 出发,经过一系列同样操作 log 的步骤,最终也必将达到一致的 state

在成功选出领导者后,下一步是日志复制。领导者负责接收客户端请求,并将每个请求作为新的日志条目添加到日志中,同时将这些日志条目复制到其他跟随者(Followers):

  1. 接收客户端请求:领导者接收来自客户端的命令,并将其作为新的日志条目添加到日志中。
  2. 日志同步:领导者通过AppendEntries RPC将新增的日志条目发送到所有跟随者,同时发送心跳信息以维持领导者角色。
  3. 日志确认:当日志条目在大多数节点上被成功复制时,领导者将条目应用到状态机,并向客户端返回结果。

上一节选主时我们提到的index其实就是日志的索引,每个 candidate 必须在 RequestVote RPC 中携带自己本地日志的最新 (term, index),如果 follower 发现这个 candidate 的日志还没有自己的新,则拒绝投票给该 candidate。

4. 日志应用

当日志条目在大多数节点上复制成功,领导者可以将日志条目应用到状态机。通过这个过程,系统状态机保持一致,确保所有节点的数据一致性。

raft在这里有最后一个重要设计:Leader 只允许 commit 包含当前 term 的日志。

这部分的具体逻辑分析比较复杂,一句话概括,是为了维护多次leader切换情况下的状态机数据安全性。