zookeeper知识必懂,从源码解读leader选举原理

本文详细介绍了Zookeeper的集群角色,包括Leader、Follower和Observer,重点解析了选举过程中的myid、epoch和zxid概念。讨论了选举选票的判断顺序及节点状态转换。通过源码分析,展示了选举过程中选票的发送、接收和判断逻辑,揭示了集群间选票流转的过程。
摘要由CSDN通过智能技术生成

zookeeper集群角色

  • leader:集群leader,负责处理事务请求,处理查询请求
  • follower:集群follower,同步leader节点数据,转化事务请求到leader,处理查询请求,参与选举投票
  • observer:与follower不同于,不参与选举的投票

myid

集群节点id属性

epoch

在每一轮投票递增,记录投票的”朝代“。

zxid(事务id)

leader节点在每次事务操作后都会递增zxid,follower节点同步leader的zxid,保证节点数据与leader节点数据一致。

节点的状态

  • looking:选举阶段
  • following:follower节点
  • observering:observer节点,不参与选举
  • leading:leader节点

leader选举选票的判断顺序

每一轮选举都会发送 myid/zxid/epoch

  • epoch最大
  • zxid最大
  • myid最大
  • 当某个节点的得票数大于集群节点数一半则成为leader

leader的选举过程

假设一个3个节点的集群,启动时节点状态为looking,这时每个节点的epoch为1,每个节点都投票自己为leader(1/zxid/1,2/zxid/1,3/zxid/1),每个节点都会收到其他节点的投票信息。这时epoch一致的情况下判断zxid,如果都一致,说明每个节点的数据都是最新的,则判断myid,修改自己节点的投票信息,以选票中的最大值修改。节点会保存有其他节点发送过来的选票,自己发送出去的投票信息。每一轮投票都递增epoch。每个节点保存自己的epoch。当节点收到其他节点发送过来的投票,判断epoch,相等则判断zxid,如果还相等再判断myid。而如果epoch不相等,则更新节点保存的自己的投票信息为对方的投票信息,并保存所有接收到的投票信息,发送自己新的选票。对接收到的投票信息进行归纳,判断接收到的选票有超过一般的选票与自己刚刚更新保存的选票信息相同的leader,则选择该节点为leader,否则继续投票。如果节点成为了follower,则需要设置节点状态为follower,再链接leader节点,再同步数据。每个节点有一个队列保存其他节点发送过来的投票信息,当还没选举出leader时,不断循环判断选票信息,直到有新的选票信息比当前保存的还有大。然后不断循环直到选出leader。

源码导读

基于3.5.5版本首先从zk服务器的入口开始,查看zkServer.sh脚本,其中启动关键的命令为

//1
    ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain"
//2
    nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
    "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
    -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \
    -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &

那么可以知道org.apache.zookeeper.server.quorum.QuorumPeerMain为启动的入口




@InterfaceAudience.Public
public class QuorumPeerMain {
   
    private static final Logger LOG = LoggerFactory.getLogger(QuorumPeerMain.class);
    private static final String USAGE = "Usage: QuorumPeerMain configfile";
    protected QuorumPeer quorumPeer;
    public static void main(String[] args) {
   
        QuorumPeerMain main = new QuorumPeerMain();
        try {
   
            main.initializeAndRun(args);
        } catch (IllegalArgumentException e) {
   
           //
      }
      //...省略代码
    }

    protected void initializeAndRun(String[] args)
        throws ConfigException, IOException, AdminServerException
    {
   
        QuorumPeerConfig config = new QuorumPeerConfig();
        if (args.length == 1) {
   
            // 解析配置文件
            config.parse(args[0]);
        }

        // ....
        if (args.length == 1 && config.isDistributed()) {
   
            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);
        }
    }

    public void runFromConfig(QuorumPeerConfig config)
            throws IOException, AdminServerException
    {
   
     // ... 
    }
}

启动方法QuorumPeerMain #runFromConfig

  public void runFromConfig(QuorumPeerConfig config)
        throws IOException, AdminServerException
{
   
  try {
   
      // 节点通信连接相关
      ServerCnxnFactory cnxnFactory = null;
      ServerCnxnFactory secureCnxnFactory = null;
      if (config.getClientPortAddress() != null) {
   
          // 默认没有指定为 NIOServerCnxnFactory
          cnxnFactory = ServerCnxnFactory.createFactory();
          cnxnFactory.configure(config.getClientPortAddress(),
                  config.getMaxClientCnxns(),
                  false);
      }

      if (config.getSecureClientPortAddress() != null) {
   
          secureCnxnFactory = ServerCnxnFactory.createFactory();
          secureCnxnFactory.configure(config.getSecureClientPortAddress(),
                  config.getMaxClientCnxns(),
                  true);
      }
      // 节点参数的配置
      quorumPeer = getQuorumPeer();
      quorumPeer.setTxnFactory(new FileTxnSnapLog(
                  config.getDataLogDir(),
                  config.getDataDir()));
      quorumPeer.enableLocalSessions(config.areLocalSessionsEnabled());
      quorumPeer.enableLocalSessionsUpgrading(
          config.isLocalSessionsUpgradingEnabled());
      //quorumPeer.setQuorumPeers(config.getAllMembers());
      // leader选举类型 默认为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.getSyncLimi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值