Zookeeper源码分析专题[3]-服务端快速选举算法及图解
分布式环境下的zk最开始是无主架构,在启动阶段会通过快速选主算法选出主节点,选举结束后,会出现Leader、Follower和Observer三种角色的节点,只有Leader、Follower有投票选主的权利,后续所有的事务性操作只能转发给Leader节点统一管理和执行
在分布式系统动,任何节点都有可能出现问题,如果此时集群的Leader挂了,Zk会选出一个新主接管集群,使用的算法是快速选举算法,快速选举算法是Paxos算法的简化版,有兴趣的可以上网搜一下这个算法,这个算法的提出者用一个故事说明了算法的可行性和可靠性具体的实现类叫FastLeaderElection,主要的方法时内部的lookForLeader()寻主算法
在分布式系统动,任何节点都有可能出现问题,如果此时集群的Leader挂了,Zk会选出一个新主接管集群,使用的算法是快速选举算法,快速选举算法是Paxos算法的简化版,有兴趣的可以上网搜一下这个算法,这个算法的提出者用一个故事说明了算法的可行性和可靠性具体的实现类叫FastLeaderElection,主要的方法时内部的lookForLeader()寻主算法
每一个节点在参与选举时,都有以下几个信息:
- 1)、服务器Id,登记在myid文件中
- 2)、选票,即要选哪一台服务器为主
- 3)、票箱,一个Map,存储的是当前分布式环境下,每一台服务器都将选票投给了谁,后续会从票箱中统计选票信息,获得每一台服务器获得的选票数量,如果某一台服务器获得的选票数量已经过半,则该服务器就是主
比较重要的信息就是选票,选票记录的信息有:
选票Vote中有以下几个信息:
- 1)、leaderId【Leader对应的服务器ID】
- 2)、leader服务器最大的操作Id,zxid
- 3)、选举纪元,peerEPoch【好像是每选举一次,纪元数量就是+1】
- 4)、选举轮次,electionEPoch
本文只介绍主流程,并图解一下集群启动时的选举步骤
1、选票PK
快速选举算法使用的网路通信技术就是BIO,后面会大致介绍一下其中的一个小点,节点会将自己的选票广播到其余节点,收到选票之后最终要的流程就是选票Pk,就是将新收到的选票和自身存储的选票进行Pk,通过选票Pk后就能确定该选谁为主,选票Pk的流程图如下:
流程不是很复杂,归纳一下选票Pk的主流程:为了便于理解,下面以集群启动为例,进行详细介绍
- 1)、先比较选票登记的纪元,纪元大的更适合为主
- 2)、纪元相等,比较选票登记的操作Id,大的更适合为主
- 3)、纪元和操作Id都相等,比较服务器Id,大的更适合为主
为了便于理解,下面以集群启动为例,进行详细介绍
2、图解Leader选举(以集群启动为例)
假设现在集群中有3台服务器,服务器Id分别为1,2,3,是新集群【比较简单】
2-1 初始投票
由于是新集群,本着大家都是自私的理念,都是自己先投给自己一票,则此时每个节点Id的票箱情况如下:
2-2 服务器1给服务器2和3发送自己的选票
服务器2和3收到选票后,会走选票Pk流程,很明显,服务器1的选票干不过2和3,此时票箱变成:
2-3 服务器2给服务器1和3发送自己的选票
同样会走选票Pk流程,选票会在服务器1中pk成功,在3中Pk失败,此时票箱变成:
2-4 服务器1选票Pk失败后
服务器1选票Pk失败后,会将新的选票【也就是跟投,别人投谁我就跟着投谁】再一次发送给服务器2和3,可以知道该选票在2和3中还是会Pk失败,但是不要紧,2和3票箱里服务器1的那张选票会被更新,此时票箱变成:
仔细看上面的图,服务器2似乎也已经达到了票数过半的要求,可以称为leader,但是就现在这种情况,按照之前的选票Pk规则,其实最应该成为leader的是3,所以在zk内部有个预测和等待的机制,如果过了一段时间,没有收到其余投票信息,那么服务器2就是主,显然,如果一切正常,等待一段时间后,后会收到服务器3发送的选票信息
2-5 服务器3给服务器1和2发送自己的选票
很明显,服务器3稳赢,所以服务器1和2会Pk失败,此时票箱变成:
2-6 服务器1和2选票Pk失败后
服务器1和2选票Pk失败后,同样会广播新的选票给别的服务器,最后票箱变成:
已经达到稳定状态,所有服务器票箱中登记的信息都显示服务器3应该为主
以上就是集群最开始启动时的选主流程,其余情况可以自行分析,主要就是如果此时集群有主,新加入的节点在寻主的过程中会自己变成Follower
3、网络管理介绍
前面说到,选举的网络就是BIO,实现类是QuorumCnxManager,但是有一个问题,由于寻主阶段,每个服务器都要和其他服务器建立连接,是不是会出现A主动连接B之后,B后面又连接了A,出现连接冗余,Zk内部针对这个问题的解决方案是:在连接建立之初,A会先先发送自己的服务器Id到对方B,B收到A的Id后和B自身的Id进行对比,如果A.ID>B.ID,则连接会主动断开,意味着只允许小的ID连接大的ID