分布式选举-Raft算法-1 Leader选举 原理
Raft理论是分布式数据一致性算法,为了便于理解Raft算法分成了4个部分:
Leader选举
日志复制
成员变更
日志压缩
此系列文章先来分析Raft Leader选举的原理及实现,在后续《分布式数据复制》的系列文章中,我们再回过头来实现Raft算法的其他功能。
Leader选举:
选举原则:典型的投票选举算法(少数服从多数),也就是说,在一定周期内获得投票最多的节点成为主节点。
节点角色:
Leader,主节点,一个任值周期内只有一个主节点。
Candidate,候选节点,集群中没有Leader时发起投票,进行选举。
Follower,跟随节点,即主节点的从节点。
消息类型:
Vote,投票消息
Heartbeat,心跳消息,Follower接收Leader的心跳消息,重置选举计时器。
选举过程:
节点刚启动时,默认是Follower状态。
启动之后,开启选举超时定时器,节点切换到Candidate状态,发起选举请求。
当Candidate状态的节点,接收到超过半数的投票,则成为主节点,切换到Leader状态。每一轮选举,每个节点只能投一次票。
Leader状态的节点向其他节点发送心跳消息,其他Candidate状态的节点切换回Follower状态,并重置选举超时定时器。
Leader节点收到更高任期号的消息,切换到Follower状态。这种情况主要发生在有网络分区时。
假设有5个节点,选举过程如下图:
初始5个节点为Follower状态,任职周期为1(Term=1)。
节点切换为Candidate状态,开启选举定时器,任值周期加1(Term=2)。先为自己投票,然后向其他节点请求投票。
得票数超过节点半数的节点,切换为Leader状态,并向其他4个节点发送心跳消息。其他4个节点切换为Follower状态。
网络分区:
下面看看,当发生网络分区时,节点状态如何切换。如下图:
网络发生A、B两个分区,A分区的3个节点继续提供服务。B分区的2个节点由于没有收到Leader节点的心跳消息,在选举定时器超时后,切换到Candidate状态,开始进行投票选举,任职周期加1(Term=3)。
当网络恢复后,A分区节点的任值周期(Term=2)比B分区节点的任值周期(Term=3)小,因此A分区的节点切换成Follower状态,5个节点开始进行投票选举,最终选举出Leader节点。
问题:如何避免网络恢复后,不发生切主?
如上图,B分区的2个节点由于永远得不到超过半数的投票,所以任职周期不断累加。当网络恢复后,原来的Leader由于任职周期小,切换为Follower状态,集群重新选主。
如何避免重新选主,将投票阶段分拆成两阶段,即预投票阶段和投票阶段。
预投票阶段:任职周期不累加,选出得票数过半的节点。
投票阶段:由在预投票阶段选出的节点发起投票请求,任职周期累加,最终选出主节点。
这样,B分区2个节点的任值周期就会小于等于原Leader的任值周期,当网络恢复后就不会重新选主。
开源软件应用:Go语言编写的etcd组件,是一个高可用,强一致的数据存储仓库,就是实现了Raft算法的选主和数据一致性,Kubernetes的选主就是使用的etcd组件。
总结:
Raft的选举算法,节点有三个角色:Leader、Candidate、Follower。有两种通信消息:投票消息,心跳消息。由选举定时器开启任值周期,在任值周期内得票过半的节点成为主节点。
优点是算法复杂度低,易于实现,主节点稳定,不会轻易发生切主。缺点是集群内节点需要全通信,通信量比较大。
Raft算法的Leader选举原理讲解完了,下一篇文章《分布式选举-Raft算法-2 Leader选举 代码实现》我们来具体看看如何用代码实现分布式环境下的Raft Leader选举。
鼓励一下,点个“在看”