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),即在收到所有服务器的封包才做出决定,而如果在一段时间内没有收集起来所有的服务器封包,干脆退出好了.理由:干脆的死或者干脆的活比起半死不活总是好些的.

posted @ 2016-10-10 18:26  hadoop_dev  阅读(337)  评论(0编辑  收藏  举报