Zookeeper Leader 选举过程

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
MSE Nacos/ZooKeeper 企业版试用,1600元额度,限量50份
简介: Zookeeper Leader 选举过程

集群概述


Zookeper  在生产环境中通常通过集群方式来部署保证高可用。下面是 Zookeeper 官网给出的一个集群部署结构图:


image.png


从上图可以得出, Zookeeper Server 的每个节点都和主节点保持通讯的,每个节点上面都存储有数据和日志的备份,只有当大多数节点可用集群才是可用的。


背景说明:


本文基于 zookeeper 3.8.0 讲解, 通过源码的维度来分析 Zookeeper 选举过程。

对于 Zookeeper 的源码编译建议参考:编译运行Zookeeper源码


集群节点状态


在分析 leader 选举之前我们先了解一下 Zookeeper 集群中节点的状态。集群节点状态定义在 QuorumPeer#ServerState 枚举,主要是包含 LOOKINGFOLLOWINGLEADINGOBSERVING 四个状态, 定义代码如下:


public enum ServerState {
    // 寻找leader状态。当服务器处于该状态时,它会认为当- 前集群中没有leader,因此需要进入leader选举状态。
    LOOKING,
    // 跟随者状态。表明当前服务器角色是 follower。
    FOLLOWING, 
    // 领导者状态。表明当前服务器角色是 leader。
    LEADING,
    // 观察者状态。表明当前服务器角色是 observer。
    OBSERVING
}


Leader 选举过程


启动源码


QuorumPeerMain 是 Zookeeper 的启动类, 通过 main 方法启动 。 启动类的作用有两个1. 读取和初始化 Zookeper 配置, 2. 启动 Zookeeper 服务


// 不展示非核心代码
public static void main(String[] args) {
    QuorumPeerMain main = new QuorumPeerMain();
    main.initializeAndRun(args);
}
protected void initializeAndRun(String[] args) throws ConfigException, IOException, AdminServerException {
    // 集群模式启动
    if (args.length == 1 && config.isDistributed()) {
        runFromConfig(config);
    } else {
    }
}
public void runFromConfig(QuorumPeerConfig config) throws IOException, AdminServerException {
    // quorumPeer 启动
  quorumPeer.start();
}


QuorumPeer 是一个线程实例类,当调用 start 方法过后会执行 QuorumPeer#run() 方法, 进行集群状态的判断当前节点是否执行选举或者同步集群节点数据信息等一系列的操作,下面是核心代码:


@Override
public void run() {
    try {
        while (running) {
            switch (getPeerState()) {
                case LOOKING:
                    // 投票给自己
                    setCurrentVote(makeLEStrategy().lookForLeader());
                    break;
                case OBSERVING:
                    // 标记为 OBSERVING
                    setObserver(makeObserver(logFactory));
                    observer.observeLeader();
                    break;
                case FOLLOWING:
                    // 标记为 FOLLOWING
                    setFollower(makeFollower(logFactory));
                    follower.followLeader();
                    break;
                case LEADING:
                    setLeader(makeLeader(logFactory));
                    leader.lead();
                    setLeader(null);
                    break;
            }
        }
    } finally {
    }
}


选举源码


FastLeaderElection 是选举的核心类 ,在这个类里面有对投票和选票的处理过程


public Vote lookForLeader() throws InterruptedException {
    // 创建一个当前选举周期的投票箱
    Map<Long, Vote> recvset = new HashMap<Long, Vote>();
    // 创建一个投票箱。这个投票箱和recvset 不一样。
    // 存储当前集群中如果已经存在Leader了的投票
    Map<Long, Vote> outofelection = new HashMap<Long, Vote>();
    int notTimeout = minNotificationInterval;
    synchronized (this) {
        // 递增本地选举周期
        logicalclock.incrementAndGet();
        // 为自己投票
        updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());
    }
    // 广播投票
    sendNotifications();
    SyncedLearnerTracker voteSet = null;
    // 如果当前服务器的状态为Looking,和stop参数为false,那么进行选举
    while ((self.getPeerState() == ServerState.LOOKING) && (!stop)) {
        if (n.electionEpoch > logicalclock.get()) {
            logicalclock.set(n.electionEpoch);
            recvset.clear();
            // totalOrderPredicate 投票 PK
            if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch, getInitId(), getInitLastLoggedZxid(), getPeerEpoch())) {
                updateProposal(n.leader, n.zxid, n.peerEpoch);
            } else {
                updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());
            }
            sendNotifications();
        } else if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch, proposedLeader, proposedZxid, proposedEpoch)) {
            updateProposal(n.leader, n.zxid, n.peerEpoch);
            sendNotifications();
        }
        // 监听通信层接收的投票
        Notification n = recvqueue.poll(notTimeout, TimeUnit.MILLISECONDS);
        // 放入投票箱
        recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));
        // 过半逻辑
        voteSet = getVoteTracker(recvset, new Vote(proposedLeader, proposedZxid, logicalclock.get(), proposedEpoch));
    }
}


totalOrderPredicate 主要是选票 PK 的逻辑,我们再来看看代码:


protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) {
    if (self.getQuorumVerifier().getWeight(newId) == 0) {
        return false;
    }
    return ((newEpoch > curEpoch)
            || ((newEpoch == curEpoch)
                && ((newZxid > curZxid)
                    || ((newZxid == curZxid)
                        && (newId > curId)))));
}


选举过程是这个样子的 ,其实官方也给出了注释:


  1. 先比较选举的届数,届数高的说明是最新一届,胜出


  1. 再比较 zxid,也就是看谁的数据最新,最新的胜出


  1. 最后比较 serverid,这是配置文件指定的,节点 id 大者胜出


选举完成后通过 sendNotifications(); 通知其他的节点。


过程总结


前面我粗略的讲解 Zookeeper 从启动过程、Leader 选举,选举结果通知等环节。总体来说 zookeeper 作为一个高性能、高可靠的分布式协调中间件,在设计思想上是非常的优秀的, 下面我们再来总结一下 投票过程多层网络架构


投票过程


通常情况下,在投票的过程中 zxid 越大越有可能成为 leader 主要是由于 zxid 越大该节点的数据越多,避免数据的同步过程中节点事务撤销和日志文件同步的比较的过程,以提升性能。下面是 5 个 zookeeper 节点选举的过程。


image.png


注:  (sid, zxid),  当前场景为 server1 ,server2 出现故障 , server3 的 zxid = 9 , server4 和 server5 的 zxid = 8.


进行两轮选举,最终选出 sever3 为 leader 节点


多层网络架构


前面的分析过程我省略 Zookeeper 节点之间通讯的 NIO 操作, 这部分简单来讲 zookeeper 将他们划分为传输层和业务层。通过 SendWorkerRecvWorker 处理网络层数据包, WorkerSenderWorkerReceiver 处理业务层的数据。


image.png


这里会涉及到多线程操作,zookeeper 在源码中也给出了大量的日志信息,对于初学者有一定的难度,对此大家可以参考下面的 Zookeeper 选举源码流程 这部分的流程图来辅助分析。


Leader 选举源码流程


我结合 Zookeeper 的源码对启动和选举的流程做了一个比较详细的梳理如下图所示。大家可以结合 Zookeeper 源码来阅读。


image.png


参考文档


  1. Apache Zookeeper 官网


  1. Zookeeper 的领导者选举机制解析


  1. 理解 Zookeeper 的 Leader 选举过程


相关文章
|
存储 负载均衡 算法
深入浅出Zookeeper源码(七):Leader选举
对于一个分布式集群来说,保证数据写入一致性最简单的方式就是依靠一个节点来调度和管理其他节点。在分布式系统中我们一般称其为Leader。
279 6
|
消息中间件 分布式计算 算法
深入理解Zookeeper系列-3.Zookeeper实现原理及Leader选举源码分析(上)
深入理解Zookeeper系列-3.Zookeeper实现原理及Leader选举源码分析
994 0
|
分布式计算 Hadoop
Hadoop-27 ZooKeeper集群 集群配置启动 3台云服务器 myid集群 zoo.cfg多节点配置 分布式协调框架 Leader Follower Observer
Hadoop-27 ZooKeeper集群 集群配置启动 3台云服务器 myid集群 zoo.cfg多节点配置 分布式协调框架 Leader Follower Observer
257 1
|
分布式计算 负载均衡 算法
Hadoop-31 ZooKeeper 内部原理 简述Leader选举 ZAB协议 一致性
Hadoop-31 ZooKeeper 内部原理 简述Leader选举 ZAB协议 一致性
146 1
|
存储 负载均衡 算法
分布式-Zookeeper-Master选举
分布式-Zookeeper-Master选举
|
存储 SQL 消息中间件
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
196 0
|
存储 数据库
zookeeper 集群环境搭建及集群选举及数据同步机制
zookeeper 集群环境搭建及集群选举及数据同步机制
397 2
|
监控 算法 网络协议
深入理解Zookeeper系列-3.Zookeeper实现原理及Leader选举源码分析(下)
深入理解Zookeeper系列-3.Zookeeper实现原理及Leader选举源码分析
198 1
Zookeeper的选举机制原理(图文深度讲解)——过半选举
Zookeeper的选举机制原理(图文深度讲解)——过半选举
1043 0
|
1月前
|
消息中间件 分布式计算 资源调度
《聊聊分布式》ZooKeeper与ZAB协议:分布式协调的核心引擎
ZooKeeper是一个开源的分布式协调服务,基于ZAB协议实现数据一致性,提供分布式锁、配置管理、领导者选举等核心功能,具有高可用、强一致和简单易用的特点,广泛应用于Kafka、Hadoop等大型分布式系统中。
下一篇
oss云网关配置