Raft协议简介
在复制组内实现主从切换的一个基本前提是复制组内各个节点的数据必须一致,否则主从切换后将会造成数据丢失。Raft协议是目前分布式领域一个非常重要的一致性协议,RocketMQ的主从切换机制也是介于Raft协议实现的。Raft协议主要包含两个部分:Leader选举和日志复制。
Leader选举
Raft协议的核心思想是在一个复制组内选举一个Leader节点,后续统一由Leader节点处理客户端的读写请求,从节点只是从Leader节点复制数据,即一个复制组在接收客户端的读写请求之前,要先从复制组中选择一个Leader节点,这个过程称为Leader选举。
Raft 协议的选举过程如下。
-
各个节点的初始状态为Follower,每个节点会设置一个计时器,每个节点的计时时间是150~300ms的一个随机值。
-
节点的计时器到期后,状态会从Follower变更为Candidate,进入该状态的节点会发起一轮投票,首先为自己投上一票,然后向集群中的其他节点发起“拉票”,期待得到超过半数的选票支持。
-
当集群内的节点收到投票请求后,如果该节点本轮未进行投票,则投赞成票,否则投反对票,然后返回结果并重置计时器继续倒数计时。如果计算器到期,则状态会由Follower变更为Candidate。
-
如果节点收到的赞成票数超过集群节点数的一半,则将状态变更为主节点。如果本轮没有节点得到集群超过半数节点的赞成票,则继续下一轮投票。
-
主节点会定时向集群内的所有从节点发送心跳包。从节点在收到心跳包后重置计时器,这是主节点维持其“统治地位”的手段。因为从节点一旦计时器到期,就会从Follower变更为Candidate,以此来尝试发起新一轮选举。
上述文字或许不够形象,Raft官方做了一个动画来动态展示Leader的选举过程,我在自己的微信公众号上对该动画进行了详细解读。 |
Raft是一个分布式领域的一致性协议,只是一个方法论,需要使用者根据协议描述通过编程语言具体实现。以下是我结合Raft协议的动画,尝试实现Raft协议时的一些想法。读者可以将其看作一个“引子”,能体现我阅读源码的技巧。
节点状态:需要引入3种节点状态,即Follower、Candidate和Leader。
进入投票状态的计时器:Follower、Candidate两个状态需要维护一个计时器,每次计时时间从150~300ms中随机选取,即每个节点每次计时过期时间大概率是不一样的。Follower状态在计时器过期后触发一轮投票。节点在收到投票请求、Leader的心跳请求并做出响应后需要重置计时器。
投票轮次(team):Candidate状态的节点每发起一轮投票,投票轮次加1。节点收到投票请求后会先和当前节点所处的投票轮次进行比较,投票轮次大的有效。
投票机制:每一轮投票中,一个节点只能为一个节点投赞成票。例如节点A中维护的投票轮次为3并且已经为节点B投了赞成票,如果收到其他节点的投票请求并且投票轮次为3,则会投反对票;如果收到轮次为4的节点则又可以投赞成票。
成为Leader的条件:得到超过集群半数节点的赞成票。例如集群中有3个节点,则必须得到2票赞成。如果其中一台服务器宕机,剩下的两个节点还能进行选主吗?答案是可以的,因为可以得到2票,超过了初始集群中3个节点数的一半,所以通常集群中的机器数量尽量为奇数。