首页 > 代码库 > ZOOKEEPER3.3.3源码分析(四)对LEADER选举过程分析的纠正

ZOOKEEPER3.3.3源码分析(四)对LEADER选举过程分析的纠正

很抱歉,之前分析的zookeeper leader选举算法有误,特此更正说明.

那里面最大的错误在于,leader选举其实不是在大多数节点通过就能选举上的,这一点与传统的paxos算法不同,因为如果这样的话,就会出现数据丢失的情况.比如某台服务器其实有最多的数据量,按照规则而言应该是leader,但是由于启动晚了,最后只能把leader让给其它的服务器.这里面存在明显的时序问题,也就是说leader服务器启动的早晚会影响整个过程.

实际上并不是这样,leader选举算法只有在收到所有参与服务器的回复才能结束.代码如下:

                        /*                         * Only proceed if the vote comes from a replica in the                         * voting view.                         */                        if(self.getVotingView().containsKey(n.sid)){                            recvset.put(n.sid, new Vote(n.leader, n.zxid, n.epoch));                            //If have received from all nodes, then terminate                            if ((self.getVotingView().size() == recvset.size()) &&                                    (self.getQuorumVerifier().getWeight(proposedLeader) != 0)){                                self.setPeerState((proposedLeader == self.getId()) ?                                        ServerState.LEADING: learningState());                                leaveInstance();                                return new Vote(proposedLeader, proposedZxid);                            } else if (termPredicate(recvset,                                    new Vote(proposedLeader, proposedZxid,                                            logicalclock))) {                                // Verify if there is any change in the proposed leader                                while((n = recvqueue.poll(finalizeWait,                                        TimeUnit.MILLISECONDS)) != null){                                    if(totalOrderPredicate(n.leader, n.zxid,                                            proposedLeader, proposedZxid)){                                        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());                                    if(LOG.isDebugEnabled()){                                        LOG.debug("About to leave FLE instance: Leader= "                                            + proposedLeader + ", Zxid = " +                                            proposedZxid + ", My id = " + self.getId()                                            + ", My state = " + self.getPeerState());                                    }                                    leaveInstance();                                    return new Vote(proposedLeader,                                            proposedZxid);                                }                            }                        }

简单的说,以上的代码分为两种情况进行处理:
1) 如果已经接收到所有服务器的封包(self.getVotingView().size() == recvset.size()),那么这台服务器选举的leader就定下来了.
2) 否则如果这个leader已经得到超过半数的服务器认同(函数termPredicate),那么当前线程将被阻塞等待一段时间(这个时间在finalizeWait定义)看看是不是还有其它服务器比当前leader更优的leader,如果经过一段时间还没有这个新的leader提出来,同样也是选举这个leader了,否则进行下一次选举.

对于过程2)我有一个疑问,同样是时序问题,如果那个真正的leader在这个时间点到来之前还没有启动,岂不是又成不了leader了?好吧,如果换了我的设计,应该全部走过程1),即在收到所有服务器的封包才做出决定,而如果在一段时间内没有收集起来所有的服务器封包,干脆退出好了.理由:干脆的死或者干脆的活比起半死不活总是好些的.

ZOOKEEPER3.3.3源码分析(四)对LEADER选举过程分析的纠正