ZooKeeper基本理论

Zookeeper是一个开源的分布式协调服务,其一致性基于ZAB协议实现,该协议借鉴了Paxos算法。Paxos算法分为准备和接受两个阶段,确保集群在分布式环境中达成一致。在选举过程中,节点通过比较提案编号来决定是否接受提议。Zookeeper的Leader选举过程中,节点不断交换投票,直到选出半数以上节点同意的领导者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言


一、Zookeeper是什么?

zk是一个开源的分布式应用程序协调服务器,为分布式系统一致性服务。其一致性通过基于paxos算法(就某项决议达成一致)实现的zab协议完成的。主要功能有:配置维护、域名管理、分布式同步、集群管理。

二、zk选举理论:paxos算法描述

paxos算法执行过程分为两个阶段:准备阶段prepare与接受阶段。

Aprepare 阶段

  1. 提案者(Proposer)准备提交一个编号为 N 的提议,于是其首先向所有表决者(Acceptor)发

送 prepare(N)请求,用于试探集群是否支持该编号的提议。

  1. 每个表决者(Acceptor)中都保存着自己曾经 accept 过的提议中的最大编号 maxN。当一个

表决者接收到其它主机发送来的 prepare(N)请求时,其会比较 N 与 maxN 的值。有以下

几种情况:

a) 若 N 小于 maxN,则说明该提议已过时,当前表决者采取不回应或回应 Error 的方

式来拒绝该 prepare 请求;

b) 若 N 大于 maxN,则说明该提议是可以接受的,当前表决者会首先将该 N 记录下来,

并将其曾经已经 accept 的编号最大的提案 Proposal(myid,maxN,value)反馈给提案者,

以向提案者展示自己支持的提案意愿。其中第一个参数 myid 表示该提案的提案者

标识 id,第二个参数表示其曾接受的提案的最大编号 maxN,第三个参数表示该提

案的真正内容 value。当然,若当前表决者还未曾 accept 过任何提议,则会将

Proposal(myid,null,null)反馈给提案者。

c) 在 prepare 阶段 N 不可能等于 maxN。这是由 N 的生成机制决定的。要获得 N 的值,

其必定会在原来数值的基础上采用同步锁方式增一。

Baccept 阶段

  1. 当提案者(Proposer)发出 prepare(N)后,若收到了超过半数的表决者(Accepter)的反馈,

那么该提案者就会将其真正的提案 Proposal(myid,N,value)发送给所有的表决者。

  1. 当表决者(Acceptor)接收到提案者发送的 Proposal(myid,N,value)提案后,会再次拿出自己

曾经 accept 过的提议中的最大编号 maxN,或曾经记录下的 prepare 的最大编号,让 N

与它们进行比较,若 N 大于等于这两个编号,则当前表决者 accept 该提案,并反馈给

提案者。若 N 小于这两个编号,则表决者采取不回应或回应 Error 的方式来拒绝该提议。

  1. 若提案者没有接收到超过半数的表决者的 accept 反馈,则有两种可能的结果产生。一

是放弃该提案,不再提出;二是重新进入 prepare 阶段,递增提案号,重新提出 prepare

请求。

  1. 若提案者接收到的反馈数量超过了半数,则其会向外广播两类信息:

a) 向曾 accept 其提案的表决者发送“可执行数据同步信号”,即让它们执行其曾接收

到的提案;

b) 向未曾向其发送 accept 反馈的表决者发送“提案 + 可执行数据同步信号”,即让

它们接受到该提案后马上执行。

提问 1:

在Prepare阶段已经比较过了,并且已经通过了,为什么在Accept阶段还需要进行比较?

若在Accept阶段有新的提案提出,此新的提案prepare(N)N的值肯定比该Accept阶段的本地MaxN值大。

提问 2:

在 Prepare 阶段与 Accept 阶段都进行了比较,为什么在发送 COMMIT 信号量时无需进

行比较?

若commit后,会将server标记为leaner,则此server不接受提案,也不会发起提案。


三、Leader选举过程

    public Vote lookForLeader() throws InterruptedException {
        try {
            self.jmxLeaderElectionBean = new LeaderElectionBean();
            MBeanRegistry.getInstance().register(
                    self.jmxLeaderElectionBean, self.jmxLocalPeerBean);
        } catch (Exception e) {
            LOG.warn("Failed to register with JMX", e);
            self.jmxLeaderElectionBean = null;
        }
        if (self.start_fle == 0) {
           self.start_fle = Time.currentElapsedTime();
        }
        try {
            //1.选举前初始化工作
            HashMap<Long, Vote> recvset = new HashMap<Long, Vote>();//外部选票

            HashMap<Long, Vote> outofelection = new HashMap<Long, Vote>();//非法选票

            int notTimeout = finalizeWait;

            //2.我选我
            synchronized(this){
                logicalclock.incrementAndGet();
                updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());
            }

            LOG.info("New election. My id =  " + self.getId() +
                    ", proposed zxid=0x" + Long.toHexString(proposedZxid));
            sendNotifications();

            /*
             * Loop in which we exchange notifications until we find a leader
             */

            //3.比较本轮选举和来自外部的选票
            while ((self.getPeerState() == ServerState.LOOKING) &&
                    (!stop)){
                /*
                 * Remove next notification from queue, times out after 2 times
                 * the termination time
                 * 选票通知队列
                 */
                Notification n = recvqueue.poll(notTimeout,
                        TimeUnit.MILLISECONDS);

                /*
                 * Sends more notifications if haven't received enough.
                 * Otherwise processes new notification.
                 */
                if(n == null){
                    //判断当前server与集群是否失联,false:失联
                    if(manager.haveDelivered()){
                        //当前server与集群未失联,但还未选出leader,则重新发送当前server选票通知,
                        //目的是为了接收其他server的选票信息(因为n=null,recvqueue已经没有选票了)
                        sendNotifications();
                    } else {
                        //当前server与集群已失联,而不用重新发送选票通知
                        //因为如果集群还未选出leader,其他server就会执行上个逻辑即if(manager.haveDelivered()){sendNotifications();}
                        //这样当前server就能收到选票通知了
                        manager.connectAll();
                    }

                    /*
                     * Exponential backoff
                     */
                    int tmpTimeOut = notTimeout*2;
                    notTimeout = (tmpTimeOut < maxNotificationInterval?
                            tmpTimeOut : maxNotificationInterval);
                    LOG.info("Notification time out: " + notTimeout);
                }
                //校验选票合法性
                else if(validVoter(n.sid) && validVoter(n.leader)) {
                    /*
                     * Only proceed if the vote comes from a replica in the
                     * voting view for a replica in the voting view.
                     */
                    switch (n.state) {
                    case LOOKING:
                        // If notification > current, replace and send messages out
                        // 来自外部的选票epoch比较大,说明自己本轮的选票已过时
                        if (n.electionEpoch > logicalclock.get()) {
                            logicalclock.set(n.electionEpoch);
                            //清空票箱
                            recvset.clear();
                            //判断当前server与n,谁更适合做leader
                            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.get()) {
                            if(LOG.isDebugEnabled()){
                                LOG.debug("Notification election epoch is smaller than logicalclock. n.electionEpoch = 0x"
                                        + Long.toHexString(n.electionEpoch)
                                        + ", logicalclock=0x" + Long.toHexString(logicalclock.get()));
                            }
                            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));

                        //4.结束本轮选举
                        //当前server选票信息在票箱中的支持率已过半,则
                        if (termPredicate(recvset,
                                new Vote(proposedLeader, proposedZxid,
                                        logicalclock.get(), proposedEpoch))) {

                            // Verify if there is any change in the proposed leader
                            //while有两个出口:
                            //1) break:说明剩下的recvqueue队列中找到了比当前server的推荐选票
                            //2) while循环条件:说明剩下的recvqueue队列中没找到比当前server的推荐选票
                            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
                             */
                            //n为null,剩下的recvqueue队列中没找到比当前server的推荐选票
                            //当前server所推荐选票是最适合的
                            if (n == null) {
                                self.setPeerState((proposedLeader == self.getId()) ?
                                        ServerState.LEADING: learningState());

                                Vote endVote = new Vote(proposedLeader,
                                                        proposedZxid,
                                                        logicalclock.get(),
                                                        proposedEpoch);
                                leaveInstance(endVote);
                                return endVote;
                            }
                        }
                        break;
                        //5.不是选举状态的处理
                    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.get()){
                            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.set(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;
                    }
                } else {
                    if (!validVoter(n.leader)) {
                        LOG.warn("Ignoring notification for non-cluster member sid {} from sid {}", n.leader, n.sid);
                    }
                    if (!validVoter(n.sid)) {
                        LOG.warn("Ignoring notification for sid {} from non-quorum member sid {}", n.leader, n.sid);
                    }
                }
            }
            return null;
        } finally {
            try {
                if(self.jmxLeaderElectionBean != null){
                    MBeanRegistry.getInstance().unregister(
                            self.jmxLeaderElectionBean);
                }
            } catch (Exception e) {
                LOG.warn("Failed to unregister with JMX", e);
            }
            self.jmxLeaderElectionBean = null;
            LOG.debug("Number of connection processing threads: {}",
                    manager.getConnectionThreadCount());
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值