zookeeper源码分析

在看分布式有关的东西,然后就有用到zookeeper,觉得好奇,就打算把源码看看。

看这篇之前,如果想先对zk有个感性认识,可以看下上一篇,本文主要是基于自己看源码时候的理解,当然可能有些地方理解有误,还有很多没有看懂的地方。但看我这篇,多少会有些理解,比如启动过程 选举过程 集群节点通信 客户端连接 集群节点数据同步

源码构建

这里就不多说了,参考:zookeeper源码构建

ZK集群模式启动过程

1:启动类:QuorumPeerMain(要设置main方法参数,就在zoo.cfg文件的路径)

​ 服务器启动的入口,按照用户的配置启动QuorumPeer

2:org.apache.zookeeper.server.quorum.QuorumPeerConfig#parseDynamicConfig方法中会创建一个QuorumVerifier(用于检查一个服务器列表能否构成一个可用的服务器集群)

	long getWeight(long id);
    boolean containsQuorum(Set<Long> set);//投票是否过半就是这里判断的
    long getVersion();
    void setVersion(long ver);
    Map<Long, QuorumServer> getAllMembers();//获取集群中所有的节点。
    Map<Long, QuorumServer> getVotingMembers();//获取集群中参与选举的节点。
    Map<Long, QuorumServer> getObservingMembers();//获取集群中Observer节点
    boolean equals(Object o);

如果zoo.cfg中没有配置分组或者权重,默认实例化的是
`org.apache.zookeeper.server.quorum.flexible.QuorumMaj#QuorumMaj(java.util.Properties)`
并会初始化一些数据,比如总共节点信息等,看下下面源码就好了,其实还是比较容易看懂的。
 public QuorumMaj(Properties props) throws ConfigException {
   
        for (Entry<Object, Object> entry : props.entrySet()) {
   
            String key = entry.getKey().toString();
            String value = entry.getValue().toString();

            if (key.startsWith("server.")) {
   
                int dot = key.indexOf('.');
                long sid = Long.parseLong(key.substring(dot + 1));
                QuorumServer qs = new QuorumServer(sid, value);
                allMembers.put(Long.valueOf(sid), qs);
                if (qs.type == LearnerType.PARTICIPANT)
                    votingMembers.put(Long.valueOf(sid), qs);
                else {
   
                    observingMembers.put(Long.valueOf(sid), qs);
                }
            } else if (key.equals("version")) {
   
                version = Long.parseLong(value, 16);
            }
        }
        half = votingMembers.size() / 2;
    }

public QuorumMaj(Map<Long, QuorumServer> allMembers) {
   
        this.allMembers = allMembers;
        for (QuorumServer qs : allMembers.values()) {
   
            if (qs.type == LearnerType.PARTICIPANT) {
   
                votingMembers.put(Long.valueOf(qs.id), qs);
            } else {
   
                observingMembers.put(Long.valueOf(qs.id), qs);
            }
        }
        half = votingMembers.size() / 2;
    }

3:QuorumPeerConfig 看下名字应该就知道,主要是节点(法定人)有关配置,但是这里要注意一点,就是要在dataDir路径下,要有一个myid,不然就没有serverId,就拿不到QuorumServer

serverId,clientPortAddress,dataDir,dataLogDir等

4:org.apache.zookeeper.server.quorum.QuorumPeerMain#runFromConfig 要集群大于一才会进入这个方法的,否则就是单机了,单机ZooKeeperServerMain是这个启动类。

  • ManagedUtil.registerLog4jMBeans() 注册日志需要的bean

  • 创建节点直接的通信:ServerCnxnFactory(管理客户端的连接),默认使用的是NIOServerCnxnFactory。当然也可以自己配置,使用Netty,觉得有必要对NIOServerCnxnFactory进行分析

    其实我个人觉得和Netty还是有些现实,毕竟都是基于NIO,首先在Netty中,我们一般也是两个线程组,一个是boss线程组,由于客户端的连接,一个是work线程组用于IO读写。ZK中的NIOServerCnxnFactory其实类似。首先看下Set<SelectorThread> selectorThreads,这是一个ZooKeeperThread线程组,就是用来进行读写的,默认数量是CPU核数的两倍(numWorkerThreads),另一个是AcceptThread acceptThread线程组,我觉得就是用来监听客户端的连接。

    		numWorkerThreads = Integer.getInteger(
                ZOOKEEPER_NIO_NUM_WORKER_THREADS, 2 * numCores);
            workerShutdownTimeoutMS = Long.getLong(
                ZOOKEEPER_NIO_SHUTDOWN_TIMEOUT, 5000);
            for(int i=0; i<numSelectorThreads; ++i) {
         
                selectorThreads.add(new SelectorThread(i));
            }
    		//ServerSocketChannle
            this.ss = ServerSocketChannel.open();
            ss.socket().setReuseAddress(true);
            LOG.info("binding to port " + addr);
            ss.socket().bind(addr);
            ss.configureBlocking(false);
            acceptThread = new AcceptThread(ss, addr, selectorThreads);
    
  • 初始化QuorumPeer,这也是一个ZK线程,看下initialize方法

    public void initialize() throws SaslException {
         
            // init quorum auth server & learner
            if (isQuorumSaslAuthEnabled()) {
         
                Set<String> authzHosts = new HashSet<String>();
                for (QuorumServer qs : getView().values()) {
         
                    authzHosts.add(qs.hostname);
                }
                authServer = new SaslQuorumAuthServer(isQuorumServerSaslAuthRequired(),
                        quorumServerLoginContext, authzHosts);
                authLearner = new SaslQuorumAuthLearner(isQuorumLearnerSaslAuthRequired(),
                        quorumServicePrincipal, quorumLearnerLoginContext);
            } else {
         
                //这两个东西目前还不知道是干嘛的
                authServer = new NullQuorumAuthServer();
                authLearner = new NullQuorumAuthLearner();
            }
    
  • 初始化完后,会对quorumPeer做很多赋值,这个东西应该是每个节点的核心了。前面做的一些初始化,比如QuorumVerifier和NIOServerCnxnFactory也都会放到quorumPeer中。

    org.apache.zookeeper.server.quorum.QuorumPeerMain#runFromConfig

    		quorumPeer.setTxnFactory(new FileTxnSnapLog(
                          config.getDataLogDir(),
                          config.getDataDir()));//初始化快照和事务的文件
              quorumPeer.enableLocalSessions(config.areLocalSessionsEnabled());
              quorumPeer.enableLocalSessionsUpgrading(
                  config.isLocalSessionsUpgradingEnabled());
              //quorumPeer.setQuorumPeers(config.getAllMembers());
              quorumPeer.setElectionType(config.getElectionAlg());
              quorumPeer.setMyid(config.getServerId());	//设置myid
              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,原来是放在这个quorumPeer中
              quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory()));
              quorumPeer.setQuorumVerifier(config.getQuorumVerifier(), false);
              if (config.getLastSeenQuorumVerifier()!=null) {
         
                  quorumPeer.setLastSeenQuorumVerifier(config.getLastSeenQuorumVerifier(), false);
              }
              quorumPeer.initConfigInZKDatabase();
              quorumPeer.setCnxnFactory(cnxnFactory);
              quorumPeer.setSecureCnxnFactory(secureCnxnFactory);
              quorumPeer.setSslQuorum(config.isSslQuorum());
              quorumPeer.setUsePortUnification(config.shouldUsePortUnification());
              quorumPeer.setLearnerType(config.getPeerType());
              quorumPeer.setSyncEnabled(config.getSyncEnabled());
              quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs());
              if (config.sslQuorumReloadCertFiles) {
         
                  quorumPeer.getX509Util().enableCertFileReloading
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值