Raft算法

转载自:https://www.jianshu.com/p/8e4bbe7e276c

  
  首先介绍下分布式共识算法。分布式共识算法是指:多个参与者 针对 某一件事 达成完全 一致 :一件事,一个结论;已达成一致的结论,不可推翻。

  目前分布式共识算法有如下:
  Paxos:被认为是分布式共识算法的根本,其他都是其变种,但是 Paxos 论文中只给出了单个提案的过程,并没有给出复制状态机中需要的 multi-paxos 的相关细节的描述,实现 Paxos 具有很高的工程复杂度(如多点可写,允许日志空洞等)。
  Zab:被应用在 Zookeeper 中,业界使用广泛,但没有抽象成通用的 library。
  Raft:以容易理解著称,业界也涌现出很多 Raft 实现,比如大名鼎鼎的 etcd, braft, tikv 等。

什么是 Raft?
  Raft 是一种更易于理解的分布式共识算法,核心协议本质上还是师承 Paxos 的精髓,不同的是依靠 Raft 模块化的拆分以及更加简化的设计,Raft 协议相对更容易实现。
  模块化的拆分主要体现在:Raft 把一致性协议划分为 Leader 选举、Membership 变更、日志复制、Snapshot 等几个几乎完全解耦的模块。
  更加简化的设计则体现在:Raft不允许类似 Paxos 中的乱序提交、简化系统中的角色状态(只有 Leader、Follower、Candidate 三种角色)、限制仅 Leader 可写入、使用随机化的超时时间来设计 Leader Election 等等。
  Raft算法中系统必须存在且同一时刻只能有一个 Leader,只有 Leader 可以接受 Clients 发过来的请求;Leader 负责主动与所有 Followers 通信,负责将“提案”发送给所有 Followers,同时收集多数派的 Followers 应答;Leader 还需向所有 Followers 主动发送心跳维持领导地位(保持存在感)。

  

1. Raft 节点状态

  每个节点有三种状态:Follower,Candidate,Leader,状态之间是互相转换的。
  每个节点上都有一个倒计时器 (Election Timeout),时间随机在 150ms ~300ms 之间。有几种情况会重设 Timeout:
  1.收到选举的请求
  2.收到 Leader 的 Heartbeat

  在 Raft 运行过程中,最主要进行两个活动:
  1.选主 Leader Election
  2.复制日志 Log Replication

  

2.选主 Leader Election

2.1 正常情况下选主

  假设现在有如图5个节点,5个节点一开始的状态都是 Follower。在一个节点election timeout到期后,这个节点的状态变成 Candidate 开始选举,它会会先投自己一票,然后向整个集群发起选举请求 (RequestVote)

  其他四个节点都返回成功,这个节点的状态由 Candidate 变成了 Leader,并在每个一小段时间后,就给所有的 Follower 发送一个 Heartbeat 以保持所有节点的状态,Follower 收到 Leader 的 Heartbeat 后重设 Timeout。心跳检测时间很短,要远远小于选举超时时间election timout。

  这是最简单的选主情况,只要有超过一半的节点投支持票了,Candidate 才会被选举为 Leader,5个节点的情况下,3个节点 (包括 Candidate 本身) 投了支持就行。
  除此之外,每个节点中还有一个字段,叫term,意思就是任期,这个term是一个全局的、连续递增的整数,每进行一次选举,term就会加一,如果candidate赢得选举,它会当leader直到此次任期结束。

2.2 Leader出故障情况下的选主

  一开始已经有一个 Leader,所有节点正常运行。Leader 出故障挂掉了,其他四个 Follower 将进行重新选主。
  4个节点的选主过程和5个节点的类似,假设在选出一个新的 Leader 后,原来的 Leader 恢复了又重新加入了,这个时候怎么处理?在 Raft 里,第几轮选举是有记录的,重新加入的 Leader 是第一轮选举 (Term 1) 选出来的,而现在的 Leader 则是 Term 2,所有原来的 Leader 会自觉降级为 Follower。

2.3 多个 Candidate 情况下的选主

  假设一开始有4个节点,都还是 Follower。这时,有两个 Follower 同时 Timeout,都变成了 Candidate 开始选举,分别给 Follower 发送了投票请求。两个 Follower 分别返回了ok,这时两个 Candidate 都只有2票,要3票才能被选成 Leader。两个 Candidate 会分别给另外一个还没有给自己投票的 Follower 发送投票请求。但是因为 Follower 在这一轮选举中,都已经投完票了,所以都拒绝了他们的请求。所以在 Term 2 没有 Leader 被选出来。
  这时,两个节点的状态是 Candidate,两个是 Follower,但是他们的倒计时器仍然在运行,最先 Timeout 的那个节点会进行发起新一轮 Term 3 的投票。两个 Follower 在 Term 3 还没投过票,所以返回 OK,这时 Candidate 一共有三票,被选为了 Leader。
  如果 Leader Heartbeat 的时间晚于另外一个 Candidate timeout 的时间,另外一个 Candidate 仍然会发送选举请求。两个 Follower 已经投完票了,拒绝了这个 Candidate 的投票请求。Leader 进行 Heartbeat, Candidate 收到后状态自动转为 Follower,完成选主。

  

3. 复制日志 Log Replication

3.1 正常情况下复制日志

  Raft 在实际应用场景中的一致性更多的是体现在不同节点之间的数据一致性,客户端发送请求到任何一个节点都能收到一致的返回,当一个节点出故障后,其他节点仍然能以已有的数据正常进行。在选主之后的复制日志就是为了达到这个目的。
  Raft算法中无论读和写都会由leader节点来处理。读也由leader来处理,leader拿到请求后,再决定由哪一个节点来处理,要么将请求分发,要么自己处理;即使client端请求的是follower节点,Follower节点也会现将请求信息转给leader,再由leader决定由哪个节点来处理。

  一开始,Leader 和 两个 Follower 都没有任何数据。客户端发送请求给 Leader,储存数据 “sally”,Leader 先将数据写在本地日志,这时候数据还是 Uncommitted (还没最终确认)。
  Leader 给两个 Follower 发送 AppendEntries 请求,数据在 Follower 上没有冲突,则将数据暂时写在本地日志,Follower 的数据也还是 Uncommitted。
  Follower将数据写到本地后,返回 OK。Leader 收到后成功返回,只要收到的成功返回的数量超过半数 (包含Leader),Leader 将数据 “sally” 的状态改成 Committed。( 这个时候 Leader 就可以返回给客户端了)。
  Leader 再次给 Follower 发送 AppendEntries 请求(相当于commit的通知),收到请求后,Follower 将本地日志里 Uncommitted 数据改成 Committed。这样就完成了一整个复制日志的过程,三个节点的数据是一致的。

  可见,Raft 协议强依赖 Leader 节点的可用性来确保集群数据的一致性。在上述过程中,主节点可能在任意阶段挂掉,看下 Raft 协议如何针对不同阶段保障数据一致性的。

  1. 数据到达 Leader 节点前
    这个阶段 Leader 挂掉不影响一致性,不多说。

  2. 数据到达 Leader 节点,但未复制到 Follower 节点
    这个阶段 Leader 挂掉,数据属于未提交状态,Client 不会收到 Ack 会认为超时失败可安全发起重试。Follower 节点上没有该数据,重新选主后 Client 重试重新提交可成功。原来的 Leader 节点恢复后作为 Follower 加入集群重新从当前任期的新 Leader 处同步数据,强制保持和 Leader 数据一致。

  3. 数据到达 Leader 节点,成功复制到 Follower 所有节点,但还未向 Leader 响应接收
    这个阶段 Leader 挂掉,虽然数据在 Follower 节点处于未提交状态(Uncommitted)但保持一致,重新选出 Leader 后可完成数据提交,此时 Client 由于不知到底提交成功没有,可重试提交。针对这种情况 Raft 要求 RPC 请求实现幂等性,也就是要实现内部去重机制。

  4. 数据到达 Leader 节点,成功复制到 Follower 部分节点,但还未向 Leader 响应接收
    这个阶段 Leader 挂掉,数据在 Follower 节点处于未提交状态(Uncommitted)且不一致,Raft 协议要求投票只能投给拥有最新数据的节点。所以拥有最新数据的节点会被选为 Leader 再强制同步数据到 Follower,数据不会丢失并最终一致。

  5. 数据到达 Leader 节点,成功复制到 Follower 所有或多数节点,数据在 Leader 处于已提交状态,但在 Follower 处于未提交状态
    这个阶段 Leader 挂掉,重新选出新 Leader 后的处理流程和阶段 3 一样。

  6. 数据到达 Leader 节点,成功复制到 Follower 所有或多数节点,数据在所有节点都处于已提交状态,但还未响应 Client
    这个阶段 Leader 挂掉,Cluster 内部数据其实已经是一致的,Client 重复重试基于幂等策略对一致性无影响。

  7. 网络分区导致的脑裂情况,出现双 Leader
    网络分区将原先的 Leader 节点和 Follower 节点分隔开,Follower 收不到 Leader 的心跳将发起选举产生新的 Leader。这时就产生了双 Leader,原先的 Leader 独自在一个区,向它提交数据不可能复制到多数节点所以永远提交不成功。向新的 Leader 提交数据可以提交成功,网络恢复后旧的 Leader 发现集群中有更新任期(Term)的新 Leader 则自动降级为 Follower 并从新 Leader 处同步数据达成集群数据一致。

  

3.2 Network Partition 情况下进行复制日志

  在 Network Partition 的情况下,部分节点之间没办法互相通信,Raft 也能保证在这种情况下数据的一致性。
  假设,一开始有 5 个节点处于同一网络状态下。Network Partition 将节点分成两边,一边有两个节点,一边三个节点。
  两个节点这边已经有 Leader 了,来自客户端的数据 “bob” 通过 Leader 同步到 Follower。因为只有两个节点,少于3个节点,所以 “bob” 的状态仍是 Uncommitted。所以在这里,服务器会返回错误给客户端。
  另外一个 Partition 有三个节点,进行重新选主。客户端数据 “tom” 发到新的 Leader,通过和上节网络状态下相似的过程,同步到另外两个 Follower。因为这个 Partition 有3个节点,超过半数,所以数据 “tom” 都 Commit 了。
  网络状态恢复,5个节点再次处于同一个网络状态下。但是这里出现了数据冲突 “bob" 和 “tom"。
  三个节点的 Leader 广播 AppendEntries,两个节点 Partition 的 Leader 自动降级为 Follower,因为这个 Partition 的数据 “bob” 没有 Commit,返回给客户端的是错误,客户端知道请求没有成功,所以 Follower 在收到 AppendEntries 请求时,可以把 “bob“ 删除,然后同步 ”tom”,通过这么一个过程,就完成了在 Network Partition 情况下的复制日志,保证了数据的一致性。

  
可以参考:
  图解Raft:https://blog.csdn.net/i6448038/article/details/85525040
  Raft 原理动画:http://thesecretlivesofdata.com/raft/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值