Leader选举概述

当超过一台ZooKeeper服务器启动,且服务器之间已经能够进行互相通信,每台服务器都试图找到一个Leader时,便需要进入Leader选举流程。ZooKeeper集群正常运行过程中,一旦选举出了Leader,那么所有服务器的集群角色一般不会发生变化(即使集群中有非Leader角色的服务器挂了或者有新机器加入到集群)。但是当Leader服务器挂了,那么整个集群将无法对外提供服务,直到新一轮的Leader选举完毕。服务器启动时期的Leader选举与运行期间的Leader选举过程基本一致。

                             

当服务器检测到当前服务器状态为looking时,就会调用FastLeaderElection.lookForLeader方法来进行Leader的重新选举:
1. 自增选举轮次
FastLeaderElection.logicalclock用于标识当前Leader的选举次数,ZooKeeper规定了所有有效的投票都必须在同一轮次中。ZooKeeper在开始新一轮的投票(调用FastLeaderElection.lookForLeader方法)时,会首先对logicalclock进行自增操作。
2. 初始化选票

LinkedBlockingQueue<ToSend> sendqueue:选票发送队列,用于保存待发送的选票;
LinkedBlockingQueue<Notification> recvqueue:选票接收队列,用于保存接收到的外部选票;
WorkerSender ws:选票发送器,后台线程;
WorkerReceiver wr:选票接收器,后台线程;

3. 发送初始化投票
每台ZooKeeper服务器都会发起第一次投票(投给自己),然后将初始化投票放入sendqueue队列中。

LinkedBlockingQueue<ToSend> sendqueue:选票发送队列,用于保存待发送的选票;
LinkedBlockingQueue<Notification> recvqueue:选票接收队列,用于保存接收到的外部选票;
WorkerSender ws:选票发送器,后台线程;
WorkerReceiver wr:选票接收器,后台线程;

4. 接收外部投票
每台ZooKeeper服务器都会不断从recvqueue队列中获取外部投票,如果服务器发现无法获取任何外部投票,那么就会立即确认是否和集群中其他服务器保持有效连接。

if (n == null) {
   if (manager.haveDelivered()) { sendNotifications(); }   //如果已经建立连接,则再次发送自己当前内部投票
   else { manager.connectAll(); }   //如果连接未建立则重连

5. 判断选举轮次
发送完初始化选票后,开始处理外部投票(其他服务器发送的投票)。ZooKeeper规定了所有有效的投票都必须在同一选举轮次中,在处理外部投票时,会根据选举轮次进行不同的处理。
外部投票的选举轮次大于内部投票
立即跟新自己的选举轮次(logicalclock),并且清空所有已经收到的投票,然后使用初始化的选票来进行PK是否变更内部投票(服务器自身当前的投票),最终将内部投票发送出去。
外部投票的选举轮次小于内部投票
忽略该外部投票,不做任何处理,继续处理下一个外部投票。
外部投票的选举轮次与内部投票一致
开始进行选票PK。
6. 选票PK
确定当前服务器是否需要变更投票(FastLeaderElection.totalOrderPredicate)

protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) {
        LOG.debug("id: " + newId + ", proposed id: " + curId + ", zxid: 0x" +
                Long.toHexString(newZxid) + ", proposed zxid: 0x" + Long.toHexString(curZxid));
        if(self.getQuorumVerifier().getWeight(newId) == 0){
            return false;
        }

        /*
         * We return true if one of the following three cases hold:
         * 1- New epoch is higher
         * 2- New epoch is the same as current epoch, but new zxid is higher
         * 3- New epoch is the same as current epoch, new zxid is the same
         *  as current zxid, but server id is higher.
         */

        return ((newEpoch > curEpoch) ||
                ((newEpoch == curEpoch) &&
                ((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))));

}

规则1:如果外部投票被推举的Leader服务器选举轮次大于内部投票(newEpoch > curEpoch),需要变更投票;
规则2:如果如果选举轮次一致,则比较两者ZXID(newZxid > curZxid),如果外部投票的ZXID大于内部投票,则需要变更投票;
规则3:如果两者的ZXID也一致,则比较两者的SID,如果外部投票的SID大于内部投票,则需要变更投票;
7. 变更投票
选票PK后,如果确定了外部投票所推举的服务器更适合成为Leader,那么就需要变更投票——使用外部投票的选票信息来覆盖内部投票。变更完成后,需要将变更后的内部投票再次发送出去。
8. 选票归档并统计
无论是否进行投票变更,都会将刚刚处理的那份外部投票放入”投票集合“recvset(Map<Long, Vote> recvset = new HashMap<Long, Vote>();)进行归档(recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));)。归档后开始本次投票统计,如果集群中有过半(n/2+1)服务器认可了当前的投票termPredicate(recvset, new Vote(proposedLeader, proposedZxid, logicalclock.get(), proposedEpoch)),则更新服务器状态并终止投票。
9. 更新服务器状态
统计投票后,如果确定有过半(n/2+1)服务器认可了当前的投票,则需要更新服务器状态。

//更新服务器状态
self.setPeerState((proposedLeader == self.getId()) ? ServerState.LEADING: learningState());
//最终的选票
Vote endVote = new Vote(proposedLeader,proposedZxid, proposedEpoch);
leaveInstance(endVote);    //清空recvqueue队列的选票

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值