Zookeeper选举整体流程源码解析

一  Zookeeper选举流程概述

     Zookeeper选举机制分为两种:第一次启动和非第一次启动。假设有5个节点,如图1.1所示:

 图1.1 ZK集群

 1.1 第一次启动

     (1)服务器1启动,发起选举。服务器1投自己一票,此时服务器1有一票,未超过半数以上票数,选举无法完成,服务器保持状态为LOOKING

    (2)服务器2启动,再次发起选举。服务器1和服务器2分别半先投给自己1票,服务器1和服务器2会通信,此时服务器1发现服务器2的myid比自己大,因此会将自己的票给服务器2。此时服务器1票数为0,服务器票数为2。服务器2的票数没有达到半数以上,选举无法完成,服务器1和2状态为LOOKING;

    (3)服务器3启动,发起一次选举。最终服务1会有0票,服务器2会有0票,服务器3会有3票,此时服务器3的票数超过半数,因此服务器3当选为leader。服务器1和服务器2的状态变为FOLLOWING,服务器3的状态变为LEADING;

    (4)随后服务器4和服务器5启动,此时已经有leader了,不再进行选举了,服务器4和服务器5更改状态为FOLLOWING。

1.2 非第一次启动 

     当集群中的一台服务器无法和leader保持连接时,会进入leader选举,而此时,集群会有以下两种状态:

    集群中已经存在leader:针对这种情况,该节点试图去选择leader时,会被其他节点告知当前服务器的leader信息。因此,该服务器仅需要和leader重新建立连接并同步状态即可。

    集群中已经不存在leader:假设集群由5个节点组成,SID分别为1,2,3,4,5;ZXID分别为9,9,8,7,6,EPOCH均为1,且此时SID为3的服务器为leader。突然,当3和5服务器发生故障时,会触发leader选举。选举规则为:EPOCH大的直接胜出,如果EPOCH相同,则ZXID大的胜出,如果ZXID相同,则SID大的胜出。

SID:服务器ID,用来标识一台服务器,ID不能重复,且和myid一致

ZXID:事务ID,用来标识一次服务器状态的变更。在某一时刻,集群中的每台机器的ZXID不一定一致

EPOCH:每个leader任期的代号。每投完一次票该数据会增加

 二 选举源码概述

    源码中的选举整体流程如图2.1所示:

 图2.1 源码选举概述图

    整个选举过程主要由两个类完成,FastLeaderElection和QuorumCnManager。FastLeaderElection负责接收选票和发送选票; QuorumCnManager负责管理节点之间的选举通信,负责选择票数的传递。

2.1 选举准备源码解析

图2.2 

    选举代码的入口在QuorumPeerMain中的runFromConfig方法中,如图2.2所示“在完成服务器启动的初始化后,会执行quorumPeer.start(),进入该方法,执行startLeaderElection()方法进入选举流程。

    如果当前节点的状态为LOOKING(getPeerState() == ServerState.LOOKING,会创建一个Vote类,该类中有id,zxid,electionEpoch等选举关键信息。随后,如图2.3所示,会通过调用createElectionAlgorithm(electionType)来创建QuorumCnxManager,该类维护了一个接收队列,recvQueue,该队列接收其他节点的投票;也维护了一个queueSendMap,该map的键为服务器ID,值为ArrayBlockingQueue队列,该队列包含了键值所指向的服务器的选票;该类还维护了一个senderWorkerMap,该map存储了一个发送线程,负责将投票发送到其他节点,还存储了一个接收线程,负责接收其他节点的投票。

   如图2.3所示,监听类QuorumCnxManager.Listener继承了线程类,在覆写的run方法中,只要未shutdown,会一直等待其他节点发送数据。

 图 2.3

 2.2 选举执行源码解析

      

 图2.4

     执行super.start(),就是执行QuorumPeer.java类中覆写的run()方法,当zookeeper启动后,首先都是Looking状态,通过选举让其中一台服务器成为Leader,其他的成为Follower 

 图2.5

     通过调用setCurrentVote(makeLEStrategy().lookForLeader())进行选举,进入lookForLeader()方法,这里进入FastLeaderElection实现的lookForLeader方法,如图2.6所示:

图2.6 

     recvset保存了每一个服务器给该节点的合法有效投票,recvset的键为服务器id,value为其他节点给我的投票信息。notTimeout为一次选举的最大等待时间,默认是0.2s。继续往下看,如图2.7所示:

 图2.7

    每发起一轮选举,都会使logicallock加1,然后调用updateProposal()更新选票信息 。更新完选票信息后,调用sendNotifications()广播选票。如图2.8所示:

 图2.8

    通过for循环遍历投票参与者,给每台服务器发送选票(就是给SID比自己大的服务器投票),通过new 一个ToSend对象创建发送选票信息,然后将该信息放入队列sendqueue中。该队列中的信息是由WorkerSender(WorkerSender是FastLeaderElection的一个内部类)线程来负责发送的,WorkerSender的run方法如图2.9所示: 

 图 2.9

     通过sendqueue.poll从队列中获取要发送的选票,然后将选票信息传给process方法进行处理,如图2.10: 

 图2.10

   通过manager.toSend发送信息,manager为类QuorumCnManager,负责管理节点之间的选举通信,负责选择票数的传递。 toSend()如图2.11所示:

图2.12 

    首先通过this.mySid==id判断是不是自己给自己投票,如果是就通过addToRecvQueue将投票信息 放进自己的RecvQueue。如果是发送给其他服务器的选票,如果其他服务器的队列消息已经存现,就将选票信息放入oldq中,否则,则放入新的bq队列中。最后通过connectOne(sid)将消息发送出去。

    在connectOne(sid)中会调用conncectOne(long sid, InetSocketAddress electionAddr)与其他节点建立连接,如图2.13所示:

 图2.14

     如图2.15所示,在conncectOne(long sid, InetSocketAddress electionAddr)方法中与其他节点建立连接,然后通过initiateConnection(sock, id)处理连接。                       

 图2.15

       在initiateConnection(sock, id)方法中调用startConnection(sock, id),并在该方法中创建并启动了发送器线程和接收器线程,如图2.16所示:

 图2.16

   有一处重要的判断if(sid>self.getId()) ,如果要发送的节点的id比自己要大,就直接关闭自己的客户端,意思就是不参与选举了。通过new SendWorker和RecvWorker创建发送和接收线程,并调用start方法启动发送和接收线程。

    如图2.17所示,SendWorker会在run方法中一直循环从发送队列SendQueue中,获取发送消息,并调用send(b)进行发送。

 图2.17

     同样的,如图2.18.在RecvWorker的run方法中,也会一直从输入流中接收消息,调用addToRecvQueue将消息添加的发送队列中,后续会将其他节点给我的票选发送的我这。 

 图2.18

   如图2.19,从 addToRecvQueue(Message msg)方法中,调用了recvQueue.add(msg)将其他节点给我的票选信息加入队列。

 图2.19

     最后回到FastLeaderElection类中查找WorkerReceiver线程,如图2.20所示,调用manager.pollRecvQueue方法从recvQueue中获取其他节点发送给我的票选信息。

    至此,选举流程的源码分析到此结束了, 本文章只分析了一个整体流程的源码,可以帮助大家对选举流程有个整体的把握。有些细节没有去分析,有兴趣的读者可以自己下载源码分析

三 总结

    选举源码流程总结如图3.1所示:

​图3.1 

         附Zookeeper入门学习视频:

https://www.bilibili.com/video/BV1to4y1C7gw                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值