Zookeeper源码解析之Server启动

来源:互联网 发布:.sit域名 无法备案 编辑:程序博客网 时间:2024/05/16 18:56

本篇讲述Zookeeper Server启动过程。Zookeeper的源码可以在Github中下载,地址是:https://github.com/apache/zookeeper/。不过是用ivy构建,所以有兴趣的话,可以安装ivy来构建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) {            LOG.error("Invalid arguments, exiting abnormally", e);            LOG.info(USAGE);            System.err.println(USAGE);            System.exit(2);        } catch (ConfigException e) {            LOG.error("Invalid config, exiting abnormally", e);            System.err.println("Invalid config, exiting abnormally");            System.exit(2);        } catch (Exception e) {            LOG.error("Unexpected exception, exiting abnormally", e);            System.exit(1);        }        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]);        }        //启动并调度清理任务,主要的配置是:autopurge.snapRetainCount(保留日志和快照文件的数量),清理任务的频率:autopurge.purgeInterval(单位:小时)        DatadirCleanupManager purgeMgr = new DatadirCleanupManager(config                .getDataDir(), config.getDataLogDir(), config                .getSnapRetainCount(), config.getPurgeInterval());        purgeMgr.start();        if (args.length == 1 && config.servers.size() > 0) {            //根据配置文件运行Zookeeper            runFromConfig(config);        } else {            LOG.warn("Either no config or no quorum defined in config, running "                    + " in standalone mode");            // there is only server in the quorum -- run as standalone            //单机模式下运行Zookeeper            ZooKeeperServerMain.main(args);        }    }

根据配置文件运行:

    public void runFromConfig(QuorumPeerConfig config) throws IOException {      try {          //注册JMX          ManagedUtil.registerLog4jMBeans();      } catch (JMException e) {          LOG.warn("Unable to register log4j JMX control", e);      }        LOG.info("Starting quorum peer");      try {          ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory();          cnxnFactory.configure(config.getClientPortAddress(),                                config.getMaxClientCnxns());          //实例化QuorumPeer          quorumPeer = new QuorumPeer();          //服务监听端口          quorumPeer.setClientPortAddress(config.getClientPortAddress());          //日志和快照存放路径          quorumPeer.setTxnFactory(new FileTxnSnapLog(                      new File(config.getDataLogDir()),                      new File(config.getDataDir())));          //Server Map          quorumPeer.setQuorumPeers(config.getServers());          //选举算法          quorumPeer.setElectionType(config.getElectionAlg());          //服务器ID          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.setQuorumVerifier(config.getQuorumVerifier());          //连接工厂,默认NIOServerCnxnFactory          quorumPeer.setCnxnFactory(cnxnFactory);          quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory()));          quorumPeer.setLearnerType(config.getPeerType());          quorumPeer.setSyncEnabled(config.getSyncEnabled());          quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs());          //启动            quorumPeer.start();          quorumPeer.join();      } catch (InterruptedException e) {          // warn, but generally this is ok          LOG.warn("Quorum Peer interrupted", e);      }    }}

启动:

    @Override    public synchronized void start() {        //日志文件和快照文件        loadDataBase();        //启动连接工厂,主要用于处理Client的请求        cnxnFactory.start();        //初始化选举,主要是开始Leader的选举        startLeaderElection();        //启动        super.start();    }

loadDataBase过程,恢复epoch数:

    private void loadDataBase() {        File updating = new File(getTxnFactory().getSnapDir(),                                 UPDATING_EPOCH_FILENAME);try {            //从文件中恢复db            zkDb.loadDataBase();            //从最新的zxid恢复epoch变量,zxid64位,前32位是epoch值,后32位是zxid            long lastProcessedZxid = zkDb.getDataTree().lastProcessedZxid;    long epochOfZxid = ZxidUtils.getEpochFromZxid(lastProcessedZxid);            try {            currentEpoch = readLongFromFile(CURRENT_EPOCH_FILENAME);                if (epochOfZxid > currentEpoch && updating.exists()) {                    LOG.info("{} found. The server was terminated after " +                             "taking a snapshot but before updating current " +                             "epoch. Setting current epoch to {}.",                             UPDATING_EPOCH_FILENAME, epochOfZxid);                    setCurrentEpoch(epochOfZxid);                    if (!updating.delete()) {                        throw new IOException("Failed to delete " +                                              updating.toString());                    }                }            } catch(FileNotFoundException e) {            // pick a reasonable epoch number            // this should only happen once when moving to a            // new code version            currentEpoch = epochOfZxid;            LOG.info(CURRENT_EPOCH_FILENAME                    + " not found! Creating with a reasonable default of {}. This should only happen when you are upgrading your installation",                    currentEpoch);            writeLongToFile(CURRENT_EPOCH_FILENAME, currentEpoch);            }            if (epochOfZxid > currentEpoch) {            throw new IOException("The current epoch, " + ZxidUtils.zxidToString(currentEpoch) + ", is older than the last zxid, " + lastProcessedZxid);            }            try {            acceptedEpoch = readLongFromFile(ACCEPTED_EPOCH_FILENAME);            } catch(FileNotFoundException e) {            // pick a reasonable epoch number            // this should only happen once when moving to a            // new code version            acceptedEpoch = epochOfZxid;            LOG.info(ACCEPTED_EPOCH_FILENAME                    + " not found! Creating with a reasonable default of {}. This should only happen when you are upgrading your installation",                    acceptedEpoch);            writeLongToFile(ACCEPTED_EPOCH_FILENAME, acceptedEpoch);            }            if (acceptedEpoch < currentEpoch) {            throw new IOException("The current epoch, " + ZxidUtils.zxidToString(currentEpoch) + " is less than the accepted epoch, " + ZxidUtils.zxidToString(acceptedEpoch));            }        } catch(IOException ie) {            LOG.error("Unable to load database on disk", ie);            throw new RuntimeException("Unable to run quorum server ", ie);        }}

初始化选举

    synchronized public void startLeaderElection() {    try {                //初始化投票实例,投票的对象是自己    currentVote = new Vote(myid, getLastLoggedZxid(), getCurrentEpoch());    } catch(IOException e) {    RuntimeException re = new RuntimeException(e.getMessage());    re.setStackTrace(e.getStackTrace());    throw re;    }        //从配置中拿自己的选举地址        for (QuorumServer p : getView().values()) {            if (p.id == myid) {                myQuorumAddr = p.addr;                break;            }        }        if (myQuorumAddr == null) {            throw new RuntimeException("My id " + myid + " not in the peer list");        }        if (electionType == 0) {            try {                udpSocket = new DatagramSocket(myQuorumAddr.getPort());                responder = new ResponderThread();                responder.start();            } catch (SocketException e) {                throw new RuntimeException(e);            }        }        //根据配置文件,初始化选举实例。默认是FastLeaderElection        this.electionAlg = createElectionAlgorithm(electionType);    }
server启动完成,就等待client去连接了。server启动核心功能就是从snapshot和log文件中恢复datatree,其核心就是zxid。

0 0
原创粉丝点击