Raft 应该是目前使用最广泛的分布式一致性算法,像目前很受欢迎的 Kubenetes 中的分布式一致性键值存储系统 etcd 就是采用的 Raft 算法。
与 Paxos 相比较, Raft 更简洁,Paxos 为了保证每个节点数据的一致性,每一次操作都需要进行至少两轮的消息交换(可以查看上一篇关于 Paxos 算法的总结)。而 Raft 不同 ,在 Raft 算法的体系中正常情况下只存在一个 Leader 节点,Leader 处理来自客户端的所有变更请求,选举 Leader 的过程可能至少需要一轮,但是在选取了 Leader 之后,每次处理来自客户端的请求就只需要一轮消息更换(下面会解释)。
关于 Raft 实现过程可以参考以下网址,动画效果更有利于理解:
##### Raft动画展示 ****************
三种状态
- Leader 领导者状态
所有的变更操作都由领导者节点去处理 - Candidater 候选者状态
可以竞争成为领导者 - follower 追随者状态
接受领导者发送的变更日志,同时也可以成为候选者
选举
选举超时
每一个追随者节点都有一颗做领导者的心,他们会随机一个时间段去考虑要不要竞争成为领导者,如果在此期间接收到领导者或候选者发来的消息,就会打断当前考虑过程,重新考虑,选举超时控制在 150ms 到 300ms 之间。
选举机制
Raft 通过心跳机制来触发领导者选取,当追随者接收到来自领导者或者候选者的有效 RPC 时,就会保持自己追随者的身份。而当在选举超时时间内没有接收到心跳消息的时候,就认为当前集群中没有领导者,自身会进入候选状态,开始新的一轮选举任期。
选举过程
- 选举超时后,追随者会成为候选人并开始新的选举任期,节点会先为自己投票,然后给其他节点发送投票请求。
- 如果接收节点在这个任期内还没有投票,并且候选人的日志比自己新,那么它会投票给候选人,并且接收节点重置其选举超时(为了最大限度确保只能有一个领导者,当接收到有候选人时,自身就不考虑竞争成为领导者)。
- 一旦候选人获得多数票,它就会成为领导者,领导者开始向其追随者发送附加条目消息,这些消息以心跳超时指定的间隔发送。
- 追随者响应每个附加条目消息,这个选举期限将持续到一个追随者停止接收心跳并成为候选人。
- 当发生脑裂(即有两个节点同时竞选领导者并各自获取到了一半的票数)情况时,会触发一次新的选举任期。
在选举过程中,候选者会一直处于候选状态,直到出现以下情形之一:
- 当前候选者赢得了选举称为领导者,开始给所有追随者发送心跳消息
- 其他的服务器成为了领导者,并接收到来自领导者的心跳消息,自身候选状态解除
- 在一段时间之内没有服务器赢得选举,例如脑裂情况,此时候选者会再次发起选举
日志复制
过程
- leader 收到来自客户端的变更请求,如将值 A 修改为 6。
- leader 将 A -> 6 的变更信息保存在日志中,但是该条信息并未提交,因此并不是真正的修改成功,不会修改节点上的值。
- leader 在下一次心跳连接时,将 A -> 6 的日志信息一并发送给所有的 follower 节点。
- follower 节点受到来自 leader 的变更信息,将 A -> 6 信息写入日志中,并回复 leader ,如果 leader 接收到了大多数 follower 节点的回复信息,那么就认为当前变更信息可以生效,会提交当前条目,并修改当前节点状态机上的值。
- 随后 leader 通知 follower 该条目已经提交,follower 接收到提交通知后修改自身节点状态机上对应的值。
- leader 回复客户端执行结果
提交的作用
提交最终表示集群中大多数的服务器完成了客户端请求的日志写入,它保证了以下两点:
- 容错:
在集群服务器部分故障,但仍有多数服务器可以正常工作的情况下,集群仍能正常处理来自客户端的请求 - 确保重叠:
即便集群中由少部分的服务器响应失败,总有一个服务器能包含之前提交的所有条目。
保证领导者保存了所有的日志条目
在上面的选举过程中和日志复制过程中,我们可以看到,只要集群中大多数服务器相应成功即可完成操作,所以,集群每个服务器中的日志并不一定完全一样。当在一个新的选举任期过程中,我们必须保证领导者中的日志保存了所有的提交的日志条目,否则之前已经提交过的日志就有可能被覆盖掉。
所以需要以下两点保证领导者的权威性:
- 日志条目只有复制到多个服务器上时(收到多个服务器的日志写入成功消息),才能提交。如果当前日志条目被提交,那么之前的日志条目也都被间接的提交了。
- 候选者只有赢得了多个服务器的投票时,才能成为领导者。其他服务器投票时会判断候选者的日志是否比自己的新,比自己的新才会投票。