Zookeeper 源码机制分析

zkServer.sh 中的main函数: org.apache.zookeeper.server.quorum.QuorumPeerMain

zkCli.sh 中的main函数: org.apache.zookeeper.ZooKeeperMain

选主

  • HashMap<Long, Vote> outofelection :  只在外部选票状态为 FOLLOWING/LEADING 时 存储推送来的选票, 添加vote;
  • HashMap<Long, Vote> recvset: 存储外部的选票;
  •  LinkedBlockingQueue<Notification> recvqueue:  接收外部选票的队列;
  • LinkedBlockingQueue<ToSend> sendqueue:  发送自己选票的队列;

key:推送过来的服务实例 myid,代表哪个服务实例的选票;  

Value:  Vote对象对应Notification,  标识该服务器推举的选票信息:
           版本默认为 version = 0x0  
           leader的myid: leader,
           leader的zxid:  zxid, 
          leader的epoch: peerEpoch,
          发送者的logicalclock: electionEpoch

算法

选票来自OBSERVING,抛弃;

选票来自FOLLOWING/LEADING

         判定 外票的选举轮次electionEpoch与自己本身的logicalclock

  1. 相同: 外票归于<recvset>中。
              判定 该外票在<recvset>中相同的选票个数符合集群配置的有效策略(过半有效) 且  在非选主状态的外票集合<outofelection>中该选票就是leader:
          true:   确定自己的角色, 清空接收外票的队列<recvqueue>, 返回该外票,跳出整个选举方法。
          false:   什么也不干。
  2. 外票归于<outofelection>中。
           判定 外票在<outofelection>中相同的选票个数符合整个集群配置的有效策略(过半有效)且  在非选主状态的选票集合<outofelection>中该选票就是leader; 
             true:   则更新当前logicalclock为选票electionEpoch, 确定自己角色, 清空接收外票队列<recvqueue>, 返回当前选票; 跳出整个选举方法。 
              false:   什么也不干。

当一个新启动的节点加入集群时,它对集群内其他节点发出投票请求,而其他节点已不处于LOOKING状态,此时其他节点回应选举结果,该节点收集这些结果到outofelection中,最终在收到合法LEADER消息且这些选票也构成选举结束条件时,该节点就结束自己的选举行为。注意到代码中会logicalclock = n.electionEpoch;更新选举轮数。

选票来自LOOKING
 比较 选票发送方的选举序列electionEpoch  与   本身时序logicalclock

  1. 大于自己 :
    1. logicalclock = n.electionEpoch;
    2. 清空已经收到的选票<recvset>; 因为有更高一级的选举序列了。选举序列比自己小的被抛弃, 自己又会更新成比自己大的, 所以<recvset>中的选票的都等于自己的当前值。
    3. 与自己初始选票比较出更优的选票, 选出来后,更新自己推举proposed的选票对象,推送出去。
      1. 推举的leader的选举序列<peerEpoch>高的;
      2. 事务zxid 高的;
      3. myid 高的 ; 
  2. 小于自己 :   丢弃;跳出switch,处理下一个Notification。
  3. 相同与自己推举proposed的选票比较出更优的选票,再更新自己推举proposed的选票对象,推送出去。

将外票保存在选票集合<recvset>中,如果更新之后的本地推荐选票满足集群配置的有效策略(过半有效) 则 从队列recvqueue中循环获取poll,比较:

  1. 有比本地推荐选票还优先级高的,再扔回recvqueue中,break出循环;
  2. 其他的抛弃 --- 其实就是把接收队列中比自己优先级低的扔掉。

处理完之后recvqueue没有了对象, 说明接收选票完结了,当前更新之后的本地推荐选票就是leader。确定自己的角色,清空接收队列,返回该选票。

 switch (n.state) {
   case LOOKING:
       // If notification > current, replace and send messages out
       if (n.electionEpoch > logicalclock) {
           logicalclock = n.electionEpoch;
           recvset.clear();
           if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,
                   getInitId(), getInitLastLoggedZxid(), getPeerEpoch())) {
               updateProposal(n.leader, n.zxid, n.peerEpoch);
           } else {
               updateProposal(getInitId(),
                       getInitLastLoggedZxid(),
                       getPeerEpoch());
           }
           sendNotifications();
       } else if (n.electionEpoch < logicalclock) {
           if(LOG.isDebugEnabled()){
               LOG.debug("Notification election epoch is smaller than logicalclock. n.electionEpoch = 0x"
                       + Long.toHexString(n.electionEpoch)
                       + ", logicalclock=0x" + Long.toHexString(logicalclock));
           }
           break;
       } else if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,
               proposedLeader, proposedZxid, proposedEpoch)) {
           updateProposal(n.leader, n.zxid, n.peerEpoch);
           sendNotifications();
       }

       if(LOG.isDebugEnabled()){
           LOG.debug("Adding vote: from=" + n.sid +
                   ", proposed leader=" + n.leader +
                   ", proposed zxid=0x" + Long.toHexString(n.zxid) +
                   ", proposed election epoch=0x" + Long.toHexString(n.electionEpoch));
       }

       recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));

       if (termPredicate(recvset,
               new Vote(proposedLeader, proposedZxid,
                       logicalclock, proposedEpoch))) {

           // Verify if there is any change in the proposed leader
           while((n = recvqueue.poll(finalizeWait,
                   TimeUnit.MILLISECONDS)) != null){
               if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,
                       proposedLeader, proposedZxid, proposedEpoch)){
                   recvqueue.put(n);
                   break;
               }
           }

           /*
            * This predicate is true once we don't read any new
            * relevant message from the reception queue
            */
           if (n == null) {
               self.setPeerState((proposedLeader == self.getId()) ?
                       ServerState.LEADING: learningState());

               Vote endVote = new Vote(proposedLeader,
                                       proposedZxid,
                                       logicalclock,
                                       proposedEpoch);
               leaveInstance(endVote);
               return endVote;
           }
       }
       break;
   case OBSERVING:
       LOG.debug("Notification from observer: " + n.sid);
       break;
   case FOLLOWING:
   case LEADING:
       /*
        * Consider all notifications from the same epoch
        * together.
        */
       if(n.electionEpoch == logicalclock){
           recvset.put(n.sid, new Vote(n.leader,
                                         n.zxid,
                                         n.electionEpoch,
                                         n.peerEpoch));
          
           if(ooePredicate(recvset, outofelection, n)) {
               self.setPeerState((n.leader == self.getId()) ?
                       ServerState.LEADING: learningState());

               Vote endVote = new Vote(n.leader, 
                       n.zxid, 
                       n.electionEpoch, 
                       n.peerEpoch);
               leaveInstance(endVote);
               return endVote;
           }
       }

       /*
        * Before joining an established ensemble, verify
        * a majority is following the same leader.
        */
       outofelection.put(n.sid, new Vote(n.version,
                                           n.leader,
                                           n.zxid,
                                           n.electionEpoch,
                                           n.peerEpoch,
                                           n.state));

       if(ooePredicate(outofelection, outofelection, n)) {
           synchronized(this){
               logicalclock = n.electionEpoch;
               self.setPeerState((n.leader == self.getId()) ?
                       ServerState.LEADING: learningState());
           }
           Vote endVote = new Vote(n.leader,
                                   n.zxid,
                                   n.electionEpoch,
                                   n.peerEpoch);
           leaveInstance(endVote);
           return endVote;
       }
       break;
   default:
       LOG.warn("Notification state unrecognized: {} (n.state), {} (n.sid)",
               n.state, n.sid);
       break;
   }


 

转载于:https://my.oschina.net/u/3434392/blog/1505826

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值