• zookeeper源码学习一——zookeeper启动


      最近正在研究zookeeper,一些心得记录一下,如有错误,还请大神指正。

    zookeeper下载地址:http://zookeeper.apache.org/releases.html,百度一下就能找到,不过还是在这里列一下。

    我认为学习一个东西,首先要理出一个头绪,否则感觉无从下手,这里我从启动开始研究,即从zkSever.sh入手。

    if [ "x$JMXDISABLE" = "x" ]
    then
        echo "JMX enabled by default" >&2
        # for some reason these two options are necessary on jdk6 on Ubuntu
        #   accord to the docs they are not necessary, but otw jconsole cannot
        #   do a local attach
        ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMain"
    else
        echo "JMX disabled by user request" >&2
        ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain"
    fi
    

      从zkSever.sh可以看出,启动入口在QuorumPeerMain中,源码如下:

      // 入口函数
    public static void main(String[] args)
      {
        QuorumPeerMain main = new QuorumPeerMain();
       //...1、启动初始化
          main.initializeAndRun(args);
       // ...
      }
    
      protected void initializeAndRun(String[] args)
        throws QuorumPeerConfig.ConfigException, IOException
      {
        // 2、加载配置文件
        QuorumPeerConfig config = new QuorumPeerConfig();
        if (args.length == 1) {
    // 解析配置文件 config.parse(args[0]); } if ((args.length == 1) && (config.servers.size() > 0)) {
        // 配置文件的信息加载至QuorumPeer runFromConfig(config); } else { LOG.warn("Either no config or no quorum defined in config, running in standalone mode"); ZooKeeperServerMain.main(args); } } public void runFromConfig(QuorumPeerConfig config) throws IOException { try { ManagedUtil.registerLog4jMBeans(); } catch (JMException e) { LOG.warn("Unable to register log4j JMX control", e); } LOG.info("Starting quorum peer"); try { NIOServerCnxn.Factory cnxnFactory = new NIOServerCnxn.Factory(config.getClientPortAddress(), config.getMaxClientCnxns()); // 3、启动QuorumPeer this.quorumPeer = new QuorumPeer(); this.quorumPeer.setClientPortAddress(config.getClientPortAddress()); //...加载各种配置信息 this.quorumPeer.start(); this.quorumPeer.join(); } catch (InterruptedException e) { LOG.warn("Quorum Peer interrupted", e); } }

      可以看出,配置文件的解析由QuorumPeerConfig 类完成,其部分源码如下:

      public void parse(String path)
        throws QuorumPeerConfig.ConfigException
      {
        File configFile = new File(path);
    
        LOG.info("Reading configuration from: " + configFile);
        try
        {
          if (!configFile.exists()) {
            throw new IllegalArgumentException(configFile.toString() + " file is missing");
          }
         // 将配置信息加载如property文件
          Properties cfg = new Properties();
          FileInputStream in = new FileInputStream(configFile);
          try {
            cfg.load(in);
          } finally {
            in.close();
          }
    
          parseProperties(cfg);
        } catch (IOException e) {
          throw new ConfigException("Error processing " + path, e);
        } catch (IllegalArgumentException e) {
          throw new ConfigException("Error processing " + path, e);
        }
      }
    
      public void parseProperties(Properties zkProp)
        throws IOException, QuorumPeerConfig.ConfigException
      {
        int clientPort = 0;
        String clientPortAddress = null;
    // 循环解析配置文件 for (Map.Entry entry : zkProp.entrySet()) { String key = entry.getKey().toString().trim(); String value = entry.getValue().toString().trim(); if (key.equals("dataDir")) { this.dataDir = value; } else if (key.equals("dataLogDir")) { this.dataLogDir = value; } else if (key.equals("clientPort")) {
        // 客户端连接的端口号 clientPort = Integer.parseInt(value); } else if (key.equals("clientPortAddress")) { clientPortAddress = value.trim(); } else if (key.equals("tickTime")) {
        // 心跳时间 this.tickTime = Integer.parseInt(value); } else if (key.equals("maxClientCnxns")) { this.maxClientCnxns = Integer.parseInt(value); } else if (key.equals("minSessionTimeout")) { this.minSessionTimeout = Integer.parseInt(value); } else if (key.equals("maxSessionTimeout")) { this.maxSessionTimeout = Integer.parseInt(value); } else if (key.equals("initLimit")) { this.initLimit = Integer.parseInt(value); } else if (key.equals("syncLimit")) { this.syncLimit = Integer.parseInt(value); } else if (key.equals("electionAlg")) {
        // 选举算法的类型,默认算法为FastLeaderElection this.electionAlg = Integer.parseInt(value); } else if (key.equals("peerType")) { if (value.toLowerCase().equals("observer")) this.peerType = QuorumPeer.LearnerType.OBSERVER; else if (value.toLowerCase().equals("participant")) { this.peerType = QuorumPeer.LearnerType.PARTICIPANT; } else throw new ConfigException("Unrecognised peertype: " + value); } //...

      回到QuorumPeerMain类的runFromConfig方法。此方法中,会将配置信息加载至QuorumPeer,并调用其start方法:

      public synchronized void start()
      {
        try {
          this.zkDb.loadDataBase();
        } catch (IOException ie) {
          LOG.fatal("Unable to load database on disk", ie);
          throw new RuntimeException("Unable to run quorum server ", ie);
        }
        this.cnxnFactory.start();
        startLeaderElection();
        super.start();
      }
    

      在start方法中,会现价在硬盘中的数据,

     this.zkDb.loadDataBase();即ZKDatabase中
      public long loadDataBase()
        throws IOException
      {
        FileTxnSnapLog.PlayBackListener listener = new FileTxnSnapLog.PlayBackListener() {
          public void onTxnLoaded(TxnHeader hdr, Record txn) {
            Request r = new Request(null, 0L, hdr.getCxid(), hdr.getType(), null, null);
    
            r.txn = txn;
            r.hdr = hdr;
            r.zxid = hdr.getZxid();
            ZKDatabase.this.addCommittedProposal(r);
          }
        };
        long zxid = this.snapLog.restore(this.dataTree, this.sessionsWithTimeouts, listener);
        this.initialized = true;
        return zxid;
      }
    

      然后开确定选类型,startLeaderElection

      public synchronized void startLeaderElection() {
        this.currentVote = new Vote(this.myid, getLastLoggedZxid());
        for (QuorumServer p : getView().values()) {
          if (p.id == this.myid) {
            this.myQuorumAddr = p.addr;
            break;
          }
        }
        if (this.myQuorumAddr == null) {
          throw new RuntimeException("My id " + this.myid + " not in the peer list");
        }
        if (this.electionType == 0) {
          try {
            this.udpSocket = new DatagramSocket(this.myQuorumAddr.getPort());
            this.responder = new ResponderThread();
            this.responder.start();
          } catch (SocketException e) {
            throw new RuntimeException(e);
          }
        }
        this.electionAlg = createElectionAlgorithm(this.electionType);//加载选举类型
    }

      然后启动run方法



  • 相关阅读:
    BeanUtils.copyProperties的用法
    Eclipse中GitLab的配置和使用入门
    认识与入门 Markdown
    mybatis基础配置
    动态规划-最长公共子串
    查找
    Entity Framework Code First ---EF Power Tool 和MySql一起使用遇到的问题
    使用TortoiseSVN碰到的几个问题(2)-冲突解决, 图标重载
    使用TortoiseSVN碰到的几个问题(1)-导入,提交,更新
    Asp.net MVC4 Step By Step(5)-使用Web API
  • 原文地址:https://www.cnblogs.com/shaohz2014/p/5042830.html
Copyright © 2020-2023  润新知