Raft

共识算法

共识是分布式系统容错的基本问题。共识涉及多个服务器数据达成一致。一旦集群中的所有节点就数据做出有效决定之后,该决定就是最终的决定。当大多数节点(超过半数支持)则集群数据进行改变。所以,如过有5台服务器,这当中如果挂掉了2台,那么集群照样运行。但如果有更多的服务器挂掉,集群就会停止运行。

什么是Raft

首先,容错和性能方面与 Paxos 相当,不同之处在于它被分解为相对独立的子问题,并且简单明了的解决了实际系统所需的所有主要部分。

相关术语介绍

Raft 中,所有的角色分为三类:

  • Leader (领袖)
  • Follower (群众)
  • Candidate (候选人)
  • Term (任期)
    类似于民主社会,领袖由民众选出。集群中所有的参与者都是群众。最初没有领袖,此时,开始发起一轮选举。与现实民主社会一致,所有参与者都是 候选人(Candidate) 所有参与者都可以参与选举,任何人都可以投票给其他参与者(包括自己),任何参与者都可以接受投票。一轮投票过后,得票数超过半数者成为新一任 Leader ,领袖开始这一届 任期 (Term),其他的参与者变为 Follower,从 Leader 处同步数据。

    Leader 选举过程

    假如,现在集群中有 A、B、C 三个节点。最开始三个节点都是 Follower 状态。集群中没有 Leader。开始一轮选举。如此开始,每个节点开始向其他节点拉票。
    例如,节点 A 向节点 BC 拉票。此时有如下三个结果。
  • Cast a : 节点 BC 把票都给 AA 顺利的成为新的 Leader
  • Cast b : 节点 BC 中有一个节点把票给 AA 把票投给自己,A 得票超过半数,也成为 Leader
  • Cast c : 节点 BC 每个人都把票投给了其他人。A 竞选失败。选举完成,将成为 Follower

注:选举完成后,如果每个节点都将票投给自己。或者,一轮投票后,没有得票超过半数者。则所有候选人开始进入睡眠状态,睡眠时长随机给定。最先从睡眠状态唤醒的候选者开始一轮新的投票,此时,在睡眠状态中的候选人不能接受投票,如此便可快速选出领袖。
选出新的 Leader 之后,Leader 定时向所有 Follower 发送 心跳 保持其统治。如果一段时间之后 Follower 没有接收到来自 Leader 的心跳,则认为 Leader 可能已经挂掉,再次开始发起选举。

控制数据一致性

Raft 协议强依赖 Leader 节点,依靠他的可用性确保集群数据的一致性。数据的流向只有从 LeaderFollower 转移。
Client 向集群的 Leader 节点提交数据后,Leader 接受的数据标记为 未提交状态(Uncommitted)。此时,Leader 并发向所有 Follower 节点复制数据,并等待接受响应,确保集群中至少有超过半数的节点已经明确接收数据后,向 Client 确认数据已经接收。
一旦向 Client 发出数据接收 Ack 响应后,表明此时数据已经进入到 已提交(Committed)Leader 再向所有 Follower 节点发送通知,告知该数据状态已提交。
图片加载失败...
在这个过程中,任意一个阶段 Leader 节点都可能挂掉。而 Raft 对这些都进行了相应的处理。保证数据的一致性。

数据到达 Leader 节点前

此时,直接返回给 Client 提交失败或提交超时。

数据到达 Leader,并未复制到 Follower。

这个阶段,数据尚处在 未提交 状态,Client 不会收到 AckClient 会认为超时,可以安全的重试。重新选举后,后续Client 请求将发送给新的 Leader处理。原来的 Leader 恢复正常后,加入集群将变为 Follower,接受当前任期的 Leader 领导,并且从 Leader 处同步数据,保持与 Leader 数据一致。

数据已向所有 Follower 同步,但尚未向 Leader 发送 Ack

这个阶段,数据还是处在 未提交 状态,但是 Follower 已经保持了一致,重新选举后,新的 Leader 完成后续操作,然而 Client 并不知道提交的成功与否,可以重试提交。针对这种情况的问题,Raft 要求 RPC 请求实现 幂等性,要求 内部去重

数据向部分 Follower 同步,但尚未向 Leader 发送 Ack

这个阶段,数据在 Follower 节点处于 未提交 状态,且不一致。Raft 要求选举投票时,只能投给拥有最新数据的节点。在选出新的 Leader 之后,再强制同步到所有的 Follower

数据已经成功复制到 Follower 多数节点,在 Leader 已提交,但在 Follower 并为提交

这个阶段,重新选举出拥有最新数据的 Follower 成为 Leader 后,强制同步数据到所有的 Follower

数据已完成提交,但未向 Client 发送 Ack

这个阶段,集群内部数据已经保持一直。实现了 幂等性 策略的重复重试,对数据一致性无影响。

脑裂情况的处理

这种情况一般是由于 网络分区LeaderFollower 分配在不同的分区,导致 Follower 无法接受 Leader 的心跳,从而发起新的选举。产生了新的 Leader。这时候,集群中就有了双 Leader
此时,旧的 Leader 独立处于一个网络分区。向它提交属于由于得不到集群其他 FollowerAck。从而,所有的提交最终都会失败。可以向新的 Leader 提交。当网络恢复之后,旧 Leader 重新加入集群,发现有更新任期的 Leader ,则自动降级为 Follower,并从新 Leader 处同步数据。

数据同步过程

数据从 Client 发送到 LeaderLeader 接受数据,数据处在未提交 (Uncommitted)状态。之后开始复制数据到所有 FollowerFollower 接受数据并向 Leader 发送 Ack 表明数据已经复制成功。如果,接受到超过半数的 Follower 的确认。则表示数据接受成功。LeaderClient 确认数据接受,将数据置换为 以提交 (Committed) 状态。同时,向所有的 Follower 发出通知,告知该数据状态已经修改。所有 Follower 确认数据已经保持一致,不一致则同步数据保持一致。

参考

Raft https://raft.github.io/raft.pdf