Zab协议(3)-选举算法选举阶段源码解析(2)

2021SC@SDUSC

回顾

上一节分析了投票阶段的数据传输和投票的一部分过程,其中经常用到QuorumPeer类,本节分析一下QuorumPeer代码

QuorumPeer介绍

该类zookeeper的Leader选举的启动类,负责创建选举算法,zk数据恢复,启动leader选举。

源码分析

    public enum ServerState {
    
    
        LOOKING, FOLLOWING, LEADING, OBSERVING;
    }

首先我们看到这个方法定义了服务器的状态,四种,期中following和leading之前的文章已经解释过,不再解释
1、LOOKING:寻找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。
2、FOLLOWING:跟随者状态。表明当前服务器角色是 Follower,参与投票。
3、LEADING:领导者状态。表明当前服务器角色是 Leader,不参与投票。
4、OBSERVING:观察者状态。表明当前服务器角色是 Observer。顾名思义,该状态只是对leader和follower的工作进行观察监听,其可以动态扩展zookeeper集群,而又不影响集群的性能,接收客户端连接,执行leader更新系统状态的命令,不影响集群的性能是因为观察者节点不参与投票,即使是观察者节点宕机了,对集群的运行状态没有影响。

接下来我们分析一下run()方法,该方法是由第一篇的末尾的

    public synchronized void start() {
    
    
        loadDataBase();
        cnxnFactory.start();        
        startLeaderElection();
        super.start();
    }

里面的 super.start()方法间接调用的。

    public void run() {
    
    
        setName("QuorumPeer" + "[myid=" + getId() + "]" +
                cnxnFactory.getLocalAddress());

        LOG.debug("Starting quorum peer");
        try {
    
    
            jmxQuorumBean = new QuorumBean(this);
            MBeanRegistry.getInstance().register(jmxQuorumBean, null);
            for(QuorumServer s: getView().values()){
    
    
                ZKMBeanInfo p;
                if (getId() == s.id) {
    
    
                    p = jmxLocalPeerBean = new LocalPeerBean(this);
                    try {
    
    
                        MBeanRegistry.getInstance().register(p, jmxQuorumBean);
                    } catch (Exception e) {
    
    
                        LOG.warn("Failed to register with JMX", e);
                        jmxLocalPeerBean = null;
                    }
                } else {
    
    
                    p = new RemotePeerBean(s);
                    try {
    
    
                        MBeanRegistry.getInstance().register(p, jmxQuorumBean);
                    } catch (Exception e) {
    
    
                        LOG.warn("Failed to register with JMX", e);
                    }
                }
            }
        } catch (Exception e) {
    
    
            LOG.warn("Failed to register with JMX", e);
            jmxQuorumBean = null;
        }

        try {
    
    
            /*
             * Main loop
             */
            while (running) {
    
    
                switch (getPeerState()) {
    
    
                case LOOKING:
                    LOG.info("LOOKING");

                    if (Boolean.getBoolean("readonlymode.enabled")) {
    
    
                        LOG.info("Attempting to start ReadOnlyZooKeeperServer");

                        // Create read-only server but don't start it immediately
                        final ReadOnlyZooKeeperServer roZk = new ReadOnlyZooKeeperServer(
                                logFactory, this,
                                new ZooKeeperServer.BasicDataTreeBuilder(),
                                this.zkDb);
    
                        // Instead of starting roZk immediately, wait some grace
                        // period before we decide we're partitioned.
                        //
                        // Thread is used here because otherwise it would require
                        // changes in each of election strategy classes which is
                        // unnecessary code coupling.
                        Thread roZkMgr = new Thread() {
    
    
                            public void run() {
    
    
                                try {
    
    
                                    // lower-bound grace period to 2 secs
                                    sleep(Math.max(2000, tickTime));
                                    if (ServerState.LOOKING.equals(getPeerState())) {
    
    
                                        roZk.startup();
                                    }
                                } catch (InterruptedException e) {
    
    
                                    LOG.info("Interrupted while attempting to start ReadOnlyZooKeeperServer, not started");
                                } catch (Exception e) {
    
    
                                    LOG.error("FAILED to start ReadOnlyZooKeeperServer", e);
                                }
                            }
                        };
                        try {
    
    
                            roZkMgr.start();
                            setBCVote(null);
                            setCurrentVote(makeLEStrategy().lookForLeader());
                        } catch (Exception e) {
    
    
                            LOG.warn("Unexpected exception",e);
                            setPeerState(ServerState.LOOKING);
                        } finally {
    
    
                            // If the thread is in the the grace period, interrupt
                            // to come out of waiting.
                            roZkMgr.interrupt();
                            roZk.shutdown();
                        }
                    } else {
    
    
                        try {
    
    
                            setBCVote(null);
                            setCurrentVote(makeLEStrategy().lookForLeader());
                        } catch (Exception e) {
    
    
                            LOG.warn("Unexpected exception", e);
                            setPeerState(ServerState.LOOKING);
                        }
                    }
                    break;
                case OBSERVING:
                    try {
    
    
                        LOG.info("OBSERVING");
                        setObserver(makeObserver(logFactory));
                        observer.observeLeader();
                    } catch (Exception e) {
    
    
                        LOG.warn("Unexpected exception",e );                        
                    } finally {
    
    
                        observer.shutdown();
                        setObserver(null);
                        setPeerState(ServerState.LOOKING);
                    }
                    break;
                case FOLLOWING:
                    try {
    
    
                        LOG.info("FOLLOWING");
                        setFollower(makeFollower(logFactory));
                        follower.followLeader();
                    } catch (Exception e) {
    
    
                        LOG.warn("Unexpected exception",e);
                    } finally {
    
    
                        follower.shutdown();
                        setFollower(null);
                        setPeerState(ServerState.LOOKING);
                    }
                    break;
                case LEADING:
                    LOG.info("LEADING");
                    try {
    
    
                        setLeader(makeLeader(logFactory));
                        leader.lead();
                        setLeader(null);
                    } catch (Exception e) {
    
    
                        LOG.warn("Unexpected exception",e);
                    } finally {
    
    
                        if (leader != null) {
    
    
                            leader.shutdown("Forcing shutdown");
                            setLeader(null);
                        }
                        setPeerState(ServerState.LOOKING);
                    }
                    break;
                }
            }
        } finally {
    
    
            LOG.warn("QuorumPeer main thread exited");
            try {
    
    
                MBeanRegistry.getInstance().unregisterAll();
            } catch (Exception e) {
    
    
                LOG.warn("Failed to unregister with JMX", e);
            }
            jmxQuorumBean = null;
            jmxLocalPeerBean = null;
        }
    }

这个代码很长,可以采用分而治之的思想,分析一下其主要有两个作用:
1.注册Leader选举相关的JMX服务:
其中JMX是一个为应用程序、设备、系统等植入管理功能的框架,能够非常方便的让Java系统对外提供运行时数据信息获取和系统管控的接口。从3.3.0版本开始,ZooKeeper也使用了标准的JMX方式对外提供时数据信息和便捷的管控接口。

LOG.debug("Starting quorum peer");
        try {
    
    
            jmxQuorumBean = new QuorumBean(this);
            MBeanRegistry.getInstance().register(jmxQuorumBean, null);
            for(QuorumServer s: getView().values()){
    
    
                ZKMBeanInfo p;
                if (getId() == s.id) {
    
    
                    p = jmxLocalPeerBean = new LocalPeerBean(this);
                    try {
    
    
                        MBeanRegistry.getInstance().register(p, jmxQuorumBean);
                    } catch (Exception e) {
    
    
                        LOG.warn("Failed to register with JMX", e);
                        jmxLocalPeerBean = null;
                    }
                } else {
    
    
                    p = new RemotePeerBean(s);
                    try {
    
    
                        MBeanRegistry.getInstance().register(p, jmxQuorumBean);
                    } catch (Exception e) {
    
    
                        LOG.warn("Failed to register with JMX", e);
                    }
                }
            }
        } catch (Exception e) {
    
    
            LOG.warn("Failed to register with JMX", e);
            jmxQuorumBean = null;
        }

2.不断判断服务器的状态是OBESEVING还是其他的LOOKING等状态
如果是LOOKING,则调用FastLeaderElection的lookForLeader()方法,正式开始选举,如果是其它状态,分别创建实例并调用对应的leader()方法

                       while (running) {
    
    
                switch (getPeerState()) {
    
    
                case LOOKING:
                    LOG.info("LOOKING");

                    if (Boolean.getBoolean("readonlymode.enabled")) {
    
    
                        LOG.info("Attempting to start ReadOnlyZooKeeperServer");

                        // Create read-only server but don't start it immediately
                        final ReadOnlyZooKeeperServer roZk = new ReadOnlyZooKeeperServer(
                                logFactory, this,
                                new ZooKeeperServer.BasicDataTreeBuilder(),
                                this.zkDb);
    
                        // Instead of starting roZk immediately, wait some grace
                        // period before we decide we're partitioned.
                        //
                        // Thread is used here because otherwise it would require
                        // changes in each of election strategy classes which is
                        // unnecessary code coupling.
                        Thread roZkMgr = new Thread() {
    
    
                            public void run() {
    
    
                                try {
    
    
                                    // lower-bound grace period to 2 secs
                                    sleep(Math.max(2000, tickTime));
                                    if (ServerState.LOOKING.equals(getPeerState())) {
    
    
                                        roZk.startup();
                                    }
                                } catch (InterruptedException e) {
    
    
                                    LOG.info("Interrupted while attempting to start ReadOnlyZooKeeperServer, not started");
                                } catch (Exception e) {
    
    
                                    LOG.error("FAILED to start ReadOnlyZooKeeperServer", e);
                                }
                            }
                        };
                        try {
    
    
                            roZkMgr.start();
                            setBCVote(null);
                            setCurrentVote(makeLEStrategy().lookForLeader());
                        } catch (Exception e) {
    
    
                            LOG.warn("Unexpected exception",e);
                            setPeerState(ServerState.LOOKING);
                        } finally {
    
    
                            // If the thread is in the the grace period, interrupt
                            // to come out of waiting.
                            roZkMgr.interrupt();
                            roZk.shutdown();
                        }
                    } else {
    
    
                        try {
    
    
                            setBCVote(null);
                            setCurrentVote(makeLEStrategy().lookForLeader());
                        } catch (Exception e) {
    
    
                            LOG.warn("Unexpected exception", e);
                            setPeerState(ServerState.LOOKING);
                        }
                    }
                    break;
                case OBSERVING:
                    try {
    
    
                        LOG.info("OBSERVING");
                        setObserver(makeObserver(logFactory));
                        observer.observeLeader();
                    } catch (Exception e) {
    
    
                        LOG.warn("Unexpected exception",e );                        
                    } finally {
    
    
                        observer.shutdown();
                        setObserver(null);
                        setPeerState(ServerState.LOOKING);
                    }
                    break;
                case FOLLOWING:
                    try {
    
    
                        LOG.info("FOLLOWING");
                        setFollower(makeFollower(logFactory));
                        follower.followLeader();
                    } catch (Exception e) {
    
    
                        LOG.warn("Unexpected exception",e);
                    } finally {
    
    
                        follower.shutdown();
                        setFollower(null);
                        setPeerState(ServerState.LOOKING);
                    }
                    break;
                case LEADING:
                    LOG.info("LEADING");
                    try {
    
    
                        setLeader(makeLeader(logFactory));
                        leader.lead();
                        setLeader(null);
                    } catch (Exception e) {
    
    
                        LOG.warn("Unexpected exception",e);
                    } finally {
    
    
                        if (leader != null) {
    
    
                            leader.shutdown("Forcing shutdown");
                            setLeader(null);
                        }
                        setPeerState(ServerState.LOOKING);
                    }
                    break;
                }
            }
        }

总结

本节介绍了一下QuorumPeer及其代码,下一节将正式进入选举。

猜你喜欢

转载自blog.csdn.net/azzin/article/details/120878013