学习笔记之ZooKeeper源码分析

本文详细分析了ZooKeeper的服务器启动过程,重点讲解了FastLeaderElection选举策略的实现,包括选举信息的发送与接收。同时,深入探讨了ZooKeeper的Watcher机制,描述了从客户端注册到触发事件的完整流程。

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

zookeeper源码分析

(个人学习笔记,如有错误欢迎指正!!!)

服务器启动

首先zookeeper的服务器启动类为 org.apache.zookeeper.server.quorum.QuorumPeerMain该类中包含 main()方法:

public static void main(String[] args) {
   
   
    QuorumPeerMain main = new QuorumPeerMain();
    try {
   
   
        //从名字上可以分析出,该方法是初始化以及运行
        main.initializeAndRun(args);
    } catch (IllegalArgumentException e) {
   
   
       ......
    } catch (ConfigException e) {
   
   
       ......
    } catch (Exception e) {
   
   
       ......
    }
    LOG.info("Exiting normally");
    System.exit(0);
}

protected void initializeAndRun(String[] args)
        throws ConfigException, IOException{
   
   
    QuorumPeerConfig config = new QuorumPeerConfig();
    if (args.length == 1) {
   
   
        //分析传入的参数
        config.parse(args[0]);
    }
	......
    if (args.length == 1 && config.servers.size() > 0) {
   
   
        //根据配置文件,进行相应的运行操作
        runFromConfig(config);
    } else {
   
   
       ......
        ZooKeeperServerMain.main(args);
    }
}

public void runFromConfig(QuorumPeerConfig config) throws IOException {
   
   
    ......
    try {
   
   
        //根据配置,对quorumPeer进行相应的信息设置,其中一般quorumPeer对应一个server
        ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory();
        cnxnFactory.configure(config.getClientPortAddress(),
                              config.getMaxClientCnxns());
        quorumPeer = new QuorumPeer();
        quorumPeer.setClientPortAddress(config.getClientPortAddress());
        ......
        //启动server
        quorumPeer.start();
        quorumPeer.join();
    } catch (InterruptedException e) {
   
   
        // warn, but generally this is ok
        LOG.warn("Quorum Peer interrupted", e);
    }
}

其中,org.apache.zookeeper.server.quorum.QuorumPeer间接继承了 java.lang.Thread类,所以该类可以看作是一个线程,start()方法便是线程的启动方法。

@Override
public synchronized void start() {
   
   
    loadDataBase();
 	//启动客户端的监听服务
    cnxnFactory.start();   
    //该方法用来启动leader选举,server在刚启动之后,需要通过leader选举来确定自己在集群中的身份
    startLeaderElection();
    super.start();
}
synchronized public void startLeaderElection() {
   
   
   try {
   
   
      //将自己设置为推荐者,(自己的id,当前最大的事务id,当前的选举轮数)
      currentVote = new Vote(myid, getLastLoggedZxid(), getCurrentEpoch());
   } catch(IOException e) {
   
   
      ......
   }
    //遍历所有的server,保存自己的地址信息
    for (QuorumServer p : getView().values()) {
   
   
        if (p.id == myid) {
   
   
            myQuorumAddr = p.addr;
            break;
        }
    }
    if (myQuorumAddr == null) {
   
   
        ......
    }
    //electionType的信息在前面配置quorumPeer信息时,已经通过config进行了设置,默认值是3,也就	//是说该部分不执行
    if (electionType == 0) {
   
   
        try {
   
   
            udpSocket = new DatagramSocket(myQuorumAddr.getPort());
            responder = new ResponderThread();
            responder.start();
        } catch (SocketException e) {
   
   
            throw new RuntimeException(e);
        }
    }
    //根据electionType的值,创建选举算法
    this.electionAlg = createElectionAlgorithm(electionType);
}

因为electionType的默认值为3,所以默认使用的leader选举策略为 FastLeaderElection

protected Election createElectionAlgorithm(int electionAlgorithm){
   
   
    Election le=null;
            
    //TODO: use a factory rather than a switch
    switch (electionAlgorithm) {
   
   
    case 0:
        le = new LeaderElection(this);
        break;
    case 1:
        le = new AuthFastLeaderElection(this);
        break;
    case 2:
        le = new AuthFastLeaderElection(this, true);
        break;
    case 3:
        //QuorumCnxManager实例主要负责将信息发送给指定的server和接受来自其他server的信息
        //该实例中维护了两种线程SendWorker和RecvWorker,分别用于发送数据和接受数据
        qcm = new QuorumCnxManager(this);
        QuorumCnxManager.Listener listener = qcm.listener;
        if(listener != null){
   
   
            listener.start();
            le = new FastLeaderElection(this, qcm);
        } else {
   
   
            LOG.error("Null listener when initializing cnx manager");
        }
        break;
    default:
        assert false;
    }
    return le;
}

创建完leader的选举策略之后,便调用了 super.start()方法,所以便会执行 run()方法:

@Override
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 {
   
   
    		//主循环,主要是根据自身的身份,作相应的循环操作
      	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;
            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值