Zookeeper源码阅读(十五) Zookeeper集群之server启动

Posted gongcomeon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Zookeeper源码阅读(十五) Zookeeper集群之server启动相关的知识,希望对你有一定的参考价值。

前言

最近又开启了一个新项目,时间比较紧,博客写的速度也比较慢,但是还是不能放松要求~希望最近周末能抽点时间把上周漏掉的博客补上~

在前一篇大致描述了单机server的启动过程后,从这一篇开始我们将开始集群server的一些机制的一些了解,主要还有servr的启动,处理链,选举等等大的模块需要完善。

流程

其实从大致的流程上来说,单机server和集群server的处理是基本一致的,都是会从主函数启动然后去初始化一些zookeeper运行必须的一些功能类,而最大的不同点也就在于集群server是分布式的,多了一个选举的过程,这个选举的过程本篇不会深入去讲,应该会分为两篇,一篇讲Zookeeper选举依赖的ZAB算法,另一篇主要说下具体的流程。

首先通过流程图看下集群server的启动过程:

技术分享图片

从图中也可以看到,确实和上一篇所说的单机server的启动流程大致一致。

总的来说,和单机server的启动过程相比,除了预启动和初始化两个状态之外,还有一个leader选举的过程。

预启动

  1. 与单机server启动过程一样,集群server启动同样以QuorumPeerMain类作为启动类;

  2. 解析zoo.cfg;

  3. 创建并启动历史文件清理器DatadirCleanupManager;

  4. 判断启动模式。

    if (args.length == 1 && config.servers.size() > 0) {
        runFromConfig(config);
    }

    可以看到,在集群模式下(zoo.cfg中配置了多个server),zk会直接开始以集群模式启动server。这部分的启动顺序和单机版的是基本一致的,它们都是共用的同一份代码。

初始化

在预启动之后便是初始化的过程。

public void runFromConfig(QuorumPeerConfig config) throws IOException {
  try {
      ManagedUtil.registerLog4jMBeans();//注册log的bean
  } catch (JMException e) {
      LOG.warn("Unable to register log4j JMX control", e);
  }

  LOG.info("Starting quorum peer");
  try {
      ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory();//创建ServerCnxnFactory
      cnxnFactory.configure(config.getClientPortAddress(),//初始化ServerCnxnFactory
                            config.getMaxClientCnxns());

      //创建并初始化QuorumPeer
      quorumPeer = getQuorumPeer();

      //初始化的数据基本都是从zoo.cfg中读到的值,方法名也叫runFromConfig
      quorumPeer.setQuorumPeers(config.getServers());
      quorumPeer.setTxnFactory(new FileTxnSnapLog(//创建数据管理器FileTxnSnapLog
              new File(config.getDataLogDir()),
              new File(config.getDataDir())));
      quorumPeer.setElectionType(config.getElectionAlg());
      quorumPeer.setMyid(config.getServerId());
      quorumPeer.setTickTime(config.getTickTime());
      quorumPeer.setInitLimit(config.getInitLimit());
      quorumPeer.setSyncLimit(config.getSyncLimit());
      quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs());
      quorumPeer.setCnxnFactory(cnxnFactory);
      quorumPeer.setQuorumVerifier(config.getQuorumVerifier());
      quorumPeer.setClientPortAddress(config.getClientPortAddress());
      quorumPeer.setMinSessionTimeout(config.getMinSessionTimeout());
      quorumPeer.setMaxSessionTimeout(config.getMaxSessionTimeout());
      quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory()));
      quorumPeer.setLearnerType(config.getPeerType());
      quorumPeer.setSyncEnabled(config.getSyncEnabled());

      // sets quorum sasl authentication configurations
      quorumPeer.setQuorumSaslEnabled(config.quorumEnableSasl);
      if(quorumPeer.isQuorumSaslAuthEnabled()){
          quorumPeer.setQuorumServerSaslRequired(config.quorumServerRequireSasl);
          quorumPeer.setQuorumLearnerSaslRequired(config.quorumLearnerRequireSasl);
          quorumPeer.setQuorumServicePrincipal(config.quorumServicePrincipal);
          quorumPeer.setQuorumServerLoginContext(config.quorumServerLoginContext);
          quorumPeer.setQuorumLearnerLoginContext(config.quorumLearnerLoginContext);
      }

      quorumPeer.setQuorumCnxnThreadsSize(config.quorumCnxnThreadsSize);
      quorumPeer.initialize();

      quorumPeer.start();
      quorumPeer.join();
  } catch (InterruptedException e) {
      // warn, but generally this is ok
      LOG.warn("Quorum Peer interrupted", e);
  }
}

通过上面的代码及注释可以看出,初始化过程分为下面几步:

  1. 创建和初始化ServerCnxnFactory;
  2. 创建QuorumPeer实例,并初始化。
    1. 创建数据管理器FileTxnSnapLog;
    2. 创建内存数据库ZKDatabase;
  3. 恢复本地数据;
  4. 启动ServerCnxnFactory线程。

3,4两步都是在quorumPeer.start();中完成的,特别要注意的是,QuorumPeer重写了thread类的start方法,所以这里调用了start方法并不是直接去调用QuorumPeer的run方法,而真正调用QuorumPeer的run方法是在QuorumPeer的start方法中super.start()这一步。

@Override
public synchronized void start() {
    loadDataBase();//恢复本地数据
    cnxnFactory.start();//启动ServerCnxnFactory线程
    startLeaderElection();//启动leader选举
    super.start();//启动QuorumPeer线程,并在
}

这里可以看到,在start的过程中,会有一步是做leader的选举的,而这也是集群和单机server启动时最大的区别,这一步将在下篇博客中专门讲一下。

而至于super.start(),这部分主要干了两个是:

  1. JMX服务注册;
  2. 检查server状态,并在适当状态进行leader选举。

这两部在下篇也会详细说。

思考

单机和集群server启动中,除了集群server启动多了一个leader选举过程外,还有个区别是单机server会读两遍zoo.cfg,其实为什么不在ZookeeperMain里“重载”main方法(写个流程差不多的方法),以QuorumPeerConfig作为参数,这样就不用读第二遍了。不过估计是因为读配置工作量比较小,所以没有做这个事。

参考

从paxos到zk

以上是关于Zookeeper源码阅读(十五) Zookeeper集群之server启动的主要内容,如果未能解决你的问题,请参考以下文章

zookeeper

ZooKeeper

Yii源码阅读笔记(三十五)

zookeeper面试题

J.U.C并发框架源码阅读(十五)CopyOnWriteArrayList

SDWebImage源码阅读(十五)UIView+WebCacheOperation