Zab协议(1)-选举算法准备阶段源码解析

2021SC@SDUSC

Zab简介

Zookeeper(后文简称zk) 是通过 Zab 协议来保证分布式事务的最终一致性。基于该协议,zk实现了一种主备模型(即Leader和Follower模型)的系统架构来保证集群中各个副本之间数据的一致性。leader 负责数据的读写,而follower只负责数据的读,如果follower遇到写操作,会提交到leader;
Zab可大致分为四个阶段:
选举:通过特定的选举算法选出leader,
发现:leader与follower通信, Leader 会维护一个 Follower 可用客户端列表。将来客户端可以和这些 Follower节点进行通信。
同步:Leader 要负责将本身的数据与 Follower 完成同步,做到多副本存储。
广播:Leader 可以接受客户端新的事务Proposal请求,将新的Proposal请求广播给所有的 Follower。

本节主要分析leader选举前准备的源码分析

源码分析

QuorumPeerMain.java是程序启动的入口。
首先注意到里面的 initializeAndRun 方法:

  		protected void initializeAndRun(String[] args)
        throws ConfigException, IOException
    {
    
    
        QuorumPeerConfig config = new QuorumPeerConfig();
        if (args.length == 1) {
    
    
            config.parse(args[0]);
        }

        // Start and schedule the the purge task
        DatadirCleanupManager purgeMgr = new DatadirCleanupManager(config
                .getDataDir(), config.getDataLogDir(), config
                .getSnapRetainCount(), config.getPurgeInterval());
        purgeMgr.start();

        if (args.length == 1 && config.servers.size() > 0) {
    
    
            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
            ZooKeeperServerMain.main(args);
        }
    }

其中的

        if (args.length == 1 && config.servers.size() > 0) {
    
    
            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
            ZooKeeperServerMain.main(args);
        }

config.servers.size() > 0是指zoo.cfg的server数量,如果数量大于0,则启动的是一个集群,否则是单机版。
我们继续往下看,我们注意到

    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 {
    
    
          ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory();
          cnxnFactory.configure(config.getClientPortAddress(),
                                config.getMaxClientCnxns());

          quorumPeer = getQuorumPeer();

          quorumPeer.setQuorumPeers(config.getServers());
          quorumPeer.setTxnFactory(new 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);
      }
    }

    // @VisibleForTesting
    protected QuorumPeer getQuorumPeer() throws SaslException {
    
    
        return new QuorumPeer();
    }

这个方法是用来读取配置以启动集群的,其中的

     quorumPeer.setElectionType(config.getElectionAlg());

代码是用来设置选举类型,指定选举算法。getElectionAlg()方法在QuorumPeerConfig.java返回electionAlg的值,而在该类中还定义了electionAlg的值为3.如图代码:

    protected int electionAlg = 3; 

接下来在QuorumPeer.java中启动选举算法

    public synchronized void start() {
    
    
        loadDataBase();
        cnxnFactory.start();        
        startLeaderElection();
        super.start();
    }

总结

至此,Zookeeper的选举算法启动前期准备已经完成。下一篇会分析zk的leader选举算法如何实现。

猜你喜欢

转载自blog.csdn.net/azzin/article/details/120685241