ZooKeeper 选举过程 源码

  • 参考:https://www.bilibili.com/video/BV1tt41137sT?p=4
  • 视频中带注释的源码:https://github.com/boomblog/zookeeper-vip1/blob/master/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
  • 启动
    • 传参
      • 脚本启动的时候 .sh 发现调用了一个类 传入配置文件参数 就启动了
      • 我们可以通过idea 加入参数,也能启动。
    • 找伙伴
      • 一开始先启动server1,server1会尝试连接server2和server3,结果都失败,然后会重试
      • 然后我们来个客户端连接的话,因为没有leader,会连上就断开连上就断开,不断重试,直到有leader。
    • 加载数据
      • 读入我们的配置
      • 从硬盘中把持久化的内容读到内存里
    • 开启socket : cnxnFactory.start()
      • 使用NIO
    • 开启领导选举
      • 选举传输层(负责传输选票)QuorumCnxManager
        • 每个服务器要向其他服务器发送选票
          • 每个服务器对应一个队列 queueSendMap,里面放发给这个服务器的选票
        • 每个服务器要从其他服务器接收选票
          • ArrayBlockingQueue<Message> recvQueue
      • 创建个listener,接收别的小伙伴的请求(创建个新线程)
    • 真正开始选举:super.start() 就是 QuorumPeer.run(): FastLeaderElection
      • 先投给自己
      • 接受其他小伙伴的选票
        • 如果拿不到别人的选票(取队列为空),才会去建立连接(connectAll)
          • 如果两个server 相互主动连接对方,怎么办?
            • 拿到对方的id,和自己的id比较,如果对方的大,关掉连接。
            • 所以永远是小的做为server端,大的作为客户端建立连接。
      • pk
        • 排序顺序:epoch zxid server_id
        • 选票其实也是长这样(epoch, zxid, server_id)
      • 投票
        • 从server n接收到一个选票,将其更新成赢了的的选票(可能是他,也可能是我自己)放进以server_id为key的map中(投票箱)
      • 统计 
        • 统计过半数
  •  
  • 跟上面的流程对应的代码:
  • org.apache.zookeeper.server.quorum.QuorumPeerMain#runFromConfig​ 
    • quorumPeer.start();​开启线程
      • org.apache.zookeeper.server.quorum.QuorumPeer#run。MainLoop,根据status做不同的事情
        • case LOOKING(刚启动就是这个,正在寻找老大) 
          • org.apache.zookeeper.server.quorum.QuorumPeer#startLeaderElection
            • currentVote = new Vote(myid, getLastLoggedZxid(), getCurrentEpoch());
              先投自己
            • this.electionAlg = createElectionAlgorithm(electionType);
              默认是用3:fastElection。然后会启动两个线程,疯狂从两个队列(WorkerSender、WorkerReceiver​)poll
              • WorkerSender:就是把选票发出去
              • WorkerReceiver:接受选票并处理
          • setCurrentVote(makeLEStrategy().lookForLeader());
            看下要不要改票。/** * Starts a new round of leader election. Whenever our QuorumPeer changes its state to LOOKING, this method is invoked, and it  sends notifications to all other peers. */开始新的一轮选举,当QuorumPeer改变状态为LOOKING,就会调用这个方法,然后会发通知给所有其他小伙伴
            • HashMap<Long, Vote> recvset
              收到的选票,也就是本地投票箱
            • HashMap<Long, Vote> outofelection 
            • 选票上写上自己
            • sendNotifications(); 选票发送。实际上是填好选票放到发送队列里。别的线程来负责发送。
            • while ((self.getPeerState() == ServerState.LOOKING) &&(!stop))
              只要还在LOOKING状态,而且程序没有停止,就一直循环下面逻辑:​
              • Notification n = recvqueue.poll
                接收队列拖出一个通知
              • 如果是空就继续发通知sendNotification
                • 如果有一个队列都清空了(haveDelivered)(queueSendMap),就再sendNotification【sendqueue(应用层,只有一个队列)和queueSendMap(传输层,分不同的小伙伴有不同的队列)】
                • 否则,尝试连接所有其他小伙伴
              • 如果收到有效选票
              • switch (n.state) 
                • case LOOKING:
                  收到的选票也在LOOKING​
                  • 如果对方的epoch比较大
                    • 那就更新自己存的
                    • 清空收到的选票recvset
                    • 按照规则排序,对方选票牛皮则跟着对方投,否则还按自己的投
                      epoch > zxid > serverid
                  • 如果对方的epoch比自己小,可能是莫名其妙哪出问题了,打debug日志然后break循环
                  • 对方的epoch跟我一样
                    • 那也按照规则排序,谁牛皮就按谁的投
                  • 把对方的选票放进投票箱recvset
                  • 如果termPredicate返回true:从recvqueue中清空选票,只要找到一张更牛皮的选票就把票放回去继续循环。如果是真的队列空了,就结束了,看看是不是自己当了老大,是的话就把state改成LEADING,否则改成FOLLOWING
                    /** * Termination predicate. Given a set of votes, determines if have * sufficient to declare the end of the election round. 瞅一眼投票箱,看看是不是足够宣告投票结束。
                    • 其中一种校验方法是获得过半数的选票
                • case FOLLOWING ||LEADING:
        • case FOLLOWING
        • case LEADING
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值