1、zookeeper启动

每探索一门新技术的时候,我们都会从方法的入口开始探索,对于zookeeper也一样,zookeeper在启动时候是通过QuorumPeerMain来作为启动入口类。我们有必要知道在启动类启动时,zookeeper做了哪些初始化和准备工作。

public static void main(String[] args) {
    QuorumPeerMain main = new QuorumPeerMain();
    try {
        //zookeeper启动入口,初始化参数配置并启动zookeeper
        main.initializeAndRun(args);
    } catch (IllegalArgumentException e) {
        LOG.error("Invalid arguments, exiting abnormally", e);
        System.exit(2);
    } catch (ConfigException e) {
        LOG.error("Invalid config, exiting abnormally", e);
        System.exit(2);
    } catch (DatadirException e) {
        LOG.error("Unable to access datadir, exiting abnormally", e);
        System.exit(3);
    } catch (AdminServerException e) {
        LOG.error("Unable to start AdminServer, exiting abnormally", e);
        System.exit(4);
    } catch (Exception e) {
        LOG.error("Unexpected exception, exiting abnormally", e);
        System.exit(1);
    }
    LOG.info("Exiting normally");
    System.exit(0);
}

我们看到main方法里通过调用initializeAndRun开始进行初始化配置和启动,所以我们跟进去看下该方法的具体行为是做了什么?

 protected void initializeAndRun(String[] args)
        throws ConfigException, IOException, AdminServerException
    {
      //  1、解析zoo.cfg配置文件,读取配置文件中的KEY-value键值对,初始化QuorumPeerConfig 
        QuorumPeerConfig config = new QuorumPeerConfig();
        if (args.length == 1) {
            config.parse(args[0]);
        }

        //创建文件清理管理器,该清理器主要负责定期清理内存快照文件和日志文件
        //snapRetainCount:至少保留文件个数
        //purgeInterval:定时任务间隔时间,只有purgeInterval大于0,该文件清理器才会开启文件清理的定时任务
        DatadirCleanupManager purgeMgr = new DatadirCleanupManager(config
                .getDataDir(), config.getDataLogDir(), config
                .getSnapRetainCount(), config.getPurgeInterval());
        purgeMgr.start();
        //quorumVerifier!=null && (!standaloneEnabled || quorumVerifier.getVotingMembers().size() > 1);
        //上面表示如果是集群配置的话,服务器将会集群方式启动
        /**zoo.cfg集群配置servers配置如下:
             servers.1=127.0.0.1:2287:3387
              servers.2=127.0.0.1:2288:3388
              servers.3=127.0.0.1:2289:3389
          ***/
        if (args.length == 1 && config.isDistributed()) {
            runFromConfig(config);//集群方式启动
        } else {
            ZooKeeperServerMain.main(args);//单机版启动
        }
    }

然后我们看下,当zookeeper以集群方式启动时,具体做了哪些准备工作?

    public void runFromConfig(QuorumPeerConfig config)
            throws IOException, AdminServerException
    {
      try {
          //注册日志管理(不用管)
          ManagedUtil.registerLog4jMBeans();
      } catch (JMException e) {
          LOG.warn("Unable to register log4j JMX control", e);
      }

      LOG.info("Starting quorum peer");
      try {
        
          ServerCnxnFactory cnxnFactory = null;
          ServerCnxnFactory secureCnxnFactory = null;
          //我们可以在zoo.cfg里通过clientPortAddress属性来配置,如果没有配置clientPortAddress的话,默认采用clientPort端口地址
          if (config.getClientPortAddress() != null) {
              //创建ServerCnxnFactory实例,ServerCnxnFactory主要负责跟客户端进行网络通信,接收客户端网络请求,比如提交事务等,默认采用NIO。
              // 可以通过配置系统参数zookeeper.serverCnxnFactory来配置它实际实现的方式
              cnxnFactory = ServerCnxnFactory.createFactory();
              //根据端口号和maxClientCnxns初始化配置ServerCnxnFactory ,这两个参数都可以通过zoo.cfg配置,分别是:
              //clientPortAddress||ClientPort:客户端发送请求的端口
              //maxClientCnxns:最大并发数,用来控制客户端高并发
              cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns(),false);
          }
          //和ServerCnxnFactory 一样,通过在ZOO.CFG中配置secureClientPortAddress或secureClientPort,可都不配置
          if (config.getSecureClientPortAddress() != null) {
              secureCnxnFactory = ServerCnxnFactory.createFactory();
              secureCnxnFactory.configure(config.getSecureClientPortAddress(),
                      config.getMaxClientCnxns(),
                      true);
          }

          quorumPeer = getQuorumPeer();
          //创建内存快照文件和事务文件日志文件的管理器FileTxnSnapLog,该管理器提供了zookeeper上层服务跟底层数据库存储的对接入口,
          // 他提供了一系列接口,用来访问日志文件和内存快站文件(具体提供了哪些访问底层存储文件的方法,我们后面会单独抽出来探究一番)
          quorumPeer.setTxnFactory(new FileTxnSnapLog(
                      config.getDataLogDir(),
                      config.getDataDir()));
          quorumPeer.enableLocalSessions(config.areLocalSessionsEnabled());
          quorumPeer.enableLocalSessionsUpgrading(
              config.isLocalSessionsUpgradingEnabled());
          //配置选举算法,不过现在一般都不配,默认采用3
          quorumPeer.setElectionType(config.getElectionAlg());
          quorumPeer.setMyid(config.getServerId());
          quorumPeer.setTickTime(config.getTickTime());//设置心跳时间
          quorumPeer.setMinSessionTimeout(config.getMinSessionTimeout());//设置最小超时时间
          quorumPeer.setMaxSessionTimeout(config.getMaxSessionTimeout());//设置最小超时时间
          quorumPeer.setInitLimit(config.getInitLimit());//初时心跳次数
          quorumPeer.setSyncLimit(config.getSyncLimit());//同步心跳次数
          quorumPeer.setConfigFileName(config.getConfigFilename());
          //创建内存数据库ZKDatabase实例,创建该数据库内存实例时会注入一个DataTree,
          // DataTree为内存数据库内真正作为保存内存数据的数据结构,维护了整个内存数据库中节点的数据结构,
          // 同时在创建DataTree时候会初始化创建根路径/zookeeper和配额管理节点/zookeeper/quota
          quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory()));
          quorumPeer.setQuorumVerifier(config.getQuorumVerifier(), false);
          if (config.getLastSeenQuorumVerifier()!=null) {
              quorumPeer.setLastSeenQuorumVerifier(config.getLastSeenQuorumVerifier(), false);
          }
          //初始化QuorumPeer的一些其它属性,包括quorumCnxnThreadsSize(线程池QuerumServer manager线程池的初始线程数)
          quorumPeer.initConfigInZKDatabase();
          quorumPeer.setCnxnFactory(cnxnFactory);
          ...
          quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs());

          // 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并开始投票选举
          quorumPeer.start();
          quorumPeer.join();
      } catch (InterruptedException e) {
          // warn, but generally this is ok
          LOG.warn("Quorum Peer interrupted", e);
      }
    }

我们再来看下它是如何创建ServerCnxnFactory呢?

  //创建ServerCnxnFactory实例,ServerCnxnFactory主要负责跟客户端进行网络通信,接收客户端网络请求,比如提交事务等,默认采用NIO。
  // 可以通过配置系统参数zookeeper.serverCnxnFactory来配置它实际实现的方式
static public ServerCnxnFactory createFactory() throws IOException {
        //获取系统参数zookeeper.serverCnxnFactory配置
        String serverCnxnFactoryName = System.getProperty(ZOOKEEPER_SERVER_CNXN_FACTORY);
        if (serverCnxnFactoryName == null) {//没有配置则默认采用NIOServerCnxnFactory
            serverCnxnFactoryName = NIOServerCnxnFactory.class.getName();
        }
        try {
            //创建NIOServerCnxnFactory
            ServerCnxnFactory serverCnxnFactory = (ServerCnxnFactory) Class.forName(serverCnxnFactoryName)
                    .getDeclaredConstructor().newInstance();
            LOG.info("Using {} as server connection factory", serverCnxnFactoryName);
            return serverCnxnFactory;
        } catch (Exception e) {
            IOException ioe = new IOException("Couldn't instantiate "
                    + serverCnxnFactoryName);
            ioe.initCause(e);
            throw ioe;
        }
    }

我么再看下quorumPeer.start做了哪些工作

 public synchronized void start() {
        //判断集群配置是否包含本机myid
        if (!getView().containsKey(myid)) {
            throw new RuntimeException("My id " + myid + " not in the peer list");
         }
         //恢复内存数据库的数据
        loadDataBase();
        //启动网络通信服务,并监听客户端的事务请求
        startServerCnxnFactory();
        try {
            adminServer.start();
        } catch (AdminServerException e) {
            LOG.warn("Problem starting AdminServer", e);
            System.out.println(e);
        }
        //开始进行leader选举
        startLeaderElection();
        //启动当前线程(QuorumPeer本身也是一个线程).具体run做了哪些,我们后面会单独讨论,只需要知道它主要用来统计票数,更新节点服务器的状态
        super.start();
    }

总结

通过上面的代码,大概可以知道,zookeeper服务器在启动时候主要做了以下工作:
  1、解析zoo.cfg配置文件,并初始化QuorumPeerConfig.
  2、创建文件清理器,根据配置参数purgeInterval决定是否开启任务定时清理内存快照文件和日志文件。
  3、创建并开启用于和客户端进行网络通信的ServerCnxnFactory,默认是NIO的方式。
  4、创建日志快照和内存快照的管理器FileTxnSnapLog,用来访问日志文件和内存快站文件。
  5、创建并初始化内存数据库ZKDatabase
  6、恢复内存数据库数据
  7、开始执行leader选举。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 在ZooKeeper服务器启动期间,首先会进行数据初始化工作,用于将存储在磁盘上的数据文件加载到ZooKeeper...
    微子Lee阅读 1,833评论 0 0
  • Zookeeper--Zookeeper是什么博客借鉴http://www.cnblogs.com/yuyijq/...
    Albert陈凯阅读 6,026评论 1 36
  • 单机服务器启动ZooKeeper服务器启动,大体分为五个主要步骤:配置文件解析、初始化数据管理器、初始化网络I/O...
    微子Lee阅读 779评论 0 0
  • 文 冯玙哲 会在夜里,轻轻的就触碰到了你的脸颊温暖的太阳里,总有无数柔情的光照耀我可爱的孩子,你会不会知道...
    冯玙哲阅读 246评论 0 3