zookeeper选举的源码过程是什么样的

79次阅读
没有评论

共计 4384 个字符,预计需要花费 11 分钟才能阅读完成。

zookeeper 选举的源码过程是什么样的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面丸趣 TV 小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

集群概述

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

从上图可以得出,zookeeper  server 的每个节点都和主节点保持通讯的,每个节点上面都存储有数据和日志的备份,只有当大多数节点可用集群才是可用的。本文主要是基于 zookeeper  3.8.0 讲解,主要是通过源码的维度来分析 zookeeper 选举过程 对于 zookeeper 的源码编译大家可以参考:编译运行 Zookeeper 源码

集群节点状态

集群节点状态定义在 QuorumPeer#ServerState 枚举,主要是包含 LOOKING、FOLLOWING、LEADING、OBSERVING   四个状态, 下面是定义的代码和说明

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

Leader 选举过程启动和初始化

QuorumPeerMain 是 zookeeper 的启动类,通过 main 方法启动

//  不展示非核心代码  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: setObserver(makeObserver(logFactory)); observer.observeLeader(); break; case 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))))); }

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

鸿蒙官方战略合作共建——HarmonyOS 技术社区

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

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

最后比较 serverid,这是配置文件指定的,节点 id 大者胜出 选举完成后通过 sendNotifications(); 通知其他的节点。

过程总结

前面我粗略的讲解 zookeeper 从启动过程在到选举,选举结果同步的,以及如何进行投票的选举结果确认过程,但是 zookeeper   作为一个高性能、高可靠的分布式协调中间件,在很多设计的细节也是非常的优秀的。

投票过程

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

注: (sid, zxid), 当前场景为  server1 ,server2 出现故障,server3 的 zxid = 9,server4 和 server5 的 zxid 为 8.   进行两轮选举,最终选出 sever3 为 leader 节点

多层网络架构

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

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

Leader 选举源码流程

结合上面的梳理,我对 zookeeper 启动和选举的流程做了一个比较详细的梳理。

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注丸趣 TV 行业资讯频道,感谢您对丸趣 TV 的支持。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-25发表,共计4384字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)