Raft 是工程上使用较为广泛的强一致性、去中心化、高可用的分布式协议,用于管理副本复制(Log Replication)。
Raft 是一种共识算法(Consensus Algorithm),所谓共识,就是多个节点对某个事情达成一致的看法,即使是在部分节点故障、网络延时、网络分割的情况下。比较有名的共识算法还有 Paxos ,但是出了名的难懂。于是在 2014 年斯坦福大学发表了新的分布式协议 Raft,与 Paxos 相比,Raft 有着基本相同运行效率,但是更容易理解,也更容易被用在系统开发上。
节点状态
Raft中节点存在三种状态,节点会根据集群的状态转变自己的状态:
- Leader:Leader 会一直工作,直到失败。Leader 节点负责处理所有客户端的请求,定期向集群中的 Follower 节点发送心跳消息,证明自己还健在。
- Follower:Follower 只响应来自其他服务器的请求。Follower 节点不处理 Client 的请求,而是将请求重定向给集群的 Leader 节点,由 Leader 节点进行请求处理。
- Candidate:如果 Follower 长时间没有收到任何通信,它将成为 Candidate 并发起选举。获得多数选票的 Candidate 成为新的 Leader。
选举 Leader Election
开始大家都是 Follwer。由于收不到 Leader 的心跳,倒计时结束,某个节点变为 Candidate。
Candidate 自己会先投自己一票,并向其他节点发送投票请求 。在收到多数(大于1/2)投票的时候,Candidate 节点就会变成 Leader 节点。这个过程就是 Leader Election。
日志复制 Log Replication
Raft 协议中所有值的变化都是通过 Leader 。
客户端通过 Leader 写入 Log Entry,Leader 节点复制 Log Entry 到 Follower 节点。这时候所有节点数据都还没有提交
在 Leader 收到 Follower 的响应后,首先会将 Leader 节点的 Log Entry 提交,并通知 Follower 节点提交自身的 Log Entry。这样在集群中就达成对此 Log Entry 的共识。
这个过程就是 Log Replication。
详细说明
选举 Leader Election
Time Out
在 Raft 中有两种超时的控制,来控制 Leader Election 过程。
- Election Timeout,是用来控制 Follower 成为 Candidate 的超时时间。每个节点 Election Timeout 是不一样的,随机的取150ms ~ 300ms,这样来减少同时变成 Candidate 的可能性。
- Heartbeat timeout,用来控制 Leader 向 Follower 发送 Append Entries 的时间间隔,这个过程也是心跳确认的过程。
正常选举
由于未收到 Leader 的心跳,所有节点都进行倒计时,由于每个节点的倒计时时长不一样,先倒计时完成的节点变成 Candidate,并开启一个新的 Term 1。
Candidate 首先会投自己一票,并向其他节点发送投票请求,由于在这个 Term 内其他节点都未投票,所以会投票给请求的 Candidate。
Candidate 节点收到相应后就变成了 Leader 节点。
在变成 Leader 节点后,会周期性的发送心跳 Heartbeat 给 Follower 节点,Follower 节点在收到 Heartbeat 会重置自身的倒计时时间。这个 Term 会一直持续到某个节点收不到 Heartbeat 变成 Candidate 才会结束。
Leader 节点故障
当 Leader 节点故障时候,Follower 节点不再收到 Heartbeat ,自然也无法重置自身的超时时间。某个节点倒计时结束后,变成 Candidate 节点,Term 也变成2,同时会向其他2个节点发送投票请求。虽然只能收到一个节点的返回,由于自己也会投自己一票,所以依然能形成“多数派”,也可以正常变成 Leader 节点,领导整个集群。
平票处理
当某个集群有如图四个节点时候,如果有两个 Follower 倒计时同时结束(这是时候 Term 是一样的),都变为 Candidate 并发起投票。由于在一个 Term 内每个节点只可以投一票,而另外两个节点又分别投票给两个节点,这样两个 Candidate 节点都获得2票(包含自己投自己),就产生了“平票”情况。
这个时候就重新启动倒计时,重新开始一个新的 Term 。如果依然有两个节点同时变成 Candidate 并且产生平票,将重复上面的过程。直至某个 Term 内某个 Candidate 节点获得“大多数”投票,变成 Leader 节点。
日志复制 Log Replication
正常情况
Leader 节点接收到客户端请求,Leader 节点将变化写入自己的 Log,在下次心跳(Heartbeat)时候,这个变化将随着心跳发送给 Follower 节点。
Leader 节点在收到 Follower 节点返回后会执行提交操作(需要集群内多数节点的回应),达成共识后真正的数据变化,并返回客户端操作结果。
在下次心跳时候 Leader 会告知 Follower 执行提交操作。
同样当客户端又发送了一个“ADD 2”请求,那么会继续一遍上述的过程,最终保证整个集群达成对“7”的共识。
产生网络分区
“网络分区”是指某一个时间内,集群内的节点由于物理网络隔离,被分成了两分区,这两分区内的节点可以互通,而两个分区之间是访问不通的。
如下图所示,A、B节点被分到一个分区,C、D、E节点被分到另外一个分区,这两个分区之间无法连通。这时候C、D、E节点由于接收不到 Leader 节点的 Heartbeat,就会产生“Leader Election”,由于C、D、E所处的分区有集群的大多数节点,所以可以顺利的选举出一个新的 Leader , Term+1。
这时候出现两个客户端,其中一个客户端向集群发送请求,并连接到了原先的 Leader-B 上。Leader-B 虽然可以正常接收请求,也会将日志复制到其他节点。但由于无法获得“多数”节点的响应,所以日志无法提交,也无法给客户端反馈。
另外一个客户端也向集群发送写请求,并连接到新选举的 Leade-E 上。可以看到 Leader-E 由于可以获得“多数”节点的响应,所以可以正常的进行日志提交及客户端反馈。
当“网络分区”恢复的时候,集群中现在的两个 Leader 将都向集群内所有节点发送 Heartbeat,由于 Leader-E 的 Term+1后,比 Leader-B 的 Term 更新。根据约定,Leader-E 将成为整个集群的 Leader,A、B 节点成为 Follower,回滚未提交的日志,并同步新 Leader 的日志。
这个过程其实主要是参考文档中的过程翻译而已,更详细的过程,及动画演示可以参看参考文档。