文章目录

  • 流程图
  • Round
  • Leader 选举
    • 服务启动时的 Leader 选举
      • 发起投票
      • 接收投票
      • 统计投票
    • Leader 崩溃触发的 Leader 选举
      • 变更服务器状态
      • 发起投票
      • 接收投票
      • 统计投票
  • 源码分析

流程图


Round


Leader 选举

Leader 服务器的作用是管理 ZooKeeper 集群中的其他服务器。

因此,如果是单独一台服务器,不构成集群规模。在 ZooKeeper 服务的运行中不会选举 Leader 服务器,也不会作为 Leader 服务器运行

我们知道一个 ZooKeeper 服务要想满足集群方式运行,至少需要三台服务器。这里我们就以三台服务器组成的 ZooKeeper 集群为例,介绍一下 Leader 服务器选举的内部过程和底层实现。

Leader 服务器的选举操作主要发生在两种情况下:

  • 第一种就是 ZooKeeper 集群服务启动的时候
  • 第二种就是在 ZooKeeper 集群中旧的 Leader 服务器失效时,这时 ZooKeeper 集群需要选举出新的 Leader 服务器

服务启动时的 Leader 选举

我们先来看下在 ZooKeeper 集群服务最初启动的时候,Leader 服务器是如何选举的。

在 ZooKeeper 集群启动时,需要在集群中的服务器之间确定一台 Leader 服务器。

当 ZooKeeper 集群中的三台服务器启动之后,首先会进行通信检查,如果集群中的服务器之间能够进行通信。集群中的三台机器开始尝试寻找集群中的 Leader 服务器并进行数据同步等操作。

如果这时没有搜索到 Leader 服务器,说明集群中不存在 Leader 服务器。这时 ZooKeeper 集群开始发起 Leader 服务器选举。

在整个 ZooKeeper 集群中 Leader 选举主要可以分为三大步骤分别是:发起投票、接收投票、统计投票。

发起投票

在 ZooKeeper 服务器集群初始化启动的时候,集群中的每一台服务器都会将自己作为 Leader 服务器进行投票。也就是每次投票时,发送的服务器的 myid(服务器标识符)和 ZXID (集群投票信息标识符)等选票信息字段都指向本机服务器

而一个投票信息就是通过这两个字段组成的。以集群中三个服务器 Serverhost1、Serverhost2、Serverhost3 为例,三个服务器的投票内容分别是:Severhost1 的投票是(1,0)、Serverhost2 服务器的投票是(2,0)、Serverhost3 服务器的投票是(3,0)。


接收投票

集群中各个服务器在发起投票的同时,也通过网络接收来自集群中其他服务器的投票信息。

在接收到网络中的投票信息后,服务器内部首先会判断该条投票信息的有效性。检查该条投票信息的时效性,是否是本轮最新的投票,并检查该条投票信息是否是处于 LOOKING 状态的服务器发出的。


统计投票

在接收到投票后,ZooKeeper 集群就该处理和统计投票结果了。

对于每条接收到的投票信息,集群中的每一台服务器都会将自己的投票信息与其接收到的 ZooKeeper 集群中的其他投票信息进行对比。

主要进行对比的内容是 ZXID,ZXID 数值比较大的投票信息优先作为 Leader 服务器。如果每个投票信息中的 ZXID 相同,就会接着比对投票信息中的 myid 信息字段,选举出 myid 较大的服务器作为 Leader 服务器

拿上面列举的三个服务器组成的集群例子来说,对于 Serverhost1,服务器的投票信息是(1,0),该服务器接收到的 Serverhost2 服务器的投票信息是(2,0)。在 ZooKeeper 集群服务运行的过程中,首先会对比 ZXID,发现结果相同之后,对比 myid,发现 Serverhost2 服务器的 myid 比较大,于是更新自己的投票信息为(2,0),并重新向 ZooKeeper 集群中的服务器发送新的投票信息。

而 Serverhost2 服务器则保留自身的投票信息,并重新向 ZooKeeper 集群服务器中发送投票信息。

而当每轮投票过后,ZooKeeper 服务都会统计集群中服务器的投票结果,判断是否有过半数的机器投出一样的信息。如果存在过半数投票信息指向的服务器,那么该台服务器就被选举为 Leader 服务器。

比如上面的例子中,ZooKeeper 集群会选举 Severhost2 服务器作为 Leader 服务器。

当 ZooKeeper 集群选举出 Leader 服务器后,ZooKeeper 集群中的服务器就开始更新自己的角色信息,除被选举成 Leader 的服务器之外,其他集群中的服务器角色变更为 Following。


Leader 崩溃触发的 Leader 选举

接下来我们再看一下在 ZooKeeper 集群服务的运行过程中,Leader 服务器是如果进行选举的。

在 ZooKeeper 集群服务的运行过程中,Leader 服务器作为处理事物性请求以及管理其他角色服务器,在 ZooKeeper 集群中起到关键的作用。

当 ZooKeeper 集群中的 Leader 服务器发生崩溃时,集群会暂停处理事务性的会话请求,直到 ZooKeeper 集群中选举出新的 Leader 服务器。

而整个 ZooKeeper 集群在重新选举 Leader 时也经过了四个过程,分别是变更服务器状态、发起投票、接收投票、统计投票。

其中,与初始化启动时 Leader 服务器的选举过程相比,变更状态和发起投票这两个阶段的实现是不同的。下面我们来分别看看这两个阶段。


变更服务器状态

与ZooKeeper 集群服务器初始化阶段不同, 在 ZooKeeper 集群服务运行的过程中,集群中每台服务器的角色已经确定了,当 Leader 服务器崩溃后 ,ZooKeeper 集群中的其他服务器会首先将自身的状态信息变为 LOOKING 状态,该状态表示服务器已经做好选举新 Leader 服务器的准备了,这之后整个 ZooKeeper 集群开始进入选举新的 Leader 服务器过程。


发起投票

ZooKeeper 集群重新选举 Leader 服务器的过程中发起投票的过程与初始化启动时发起投票的过程基本相同。首先每个集群中的服务器都会投票给自己,将投票信息中的 Zxid 和 myid 分别指向本机服务器。


接收投票

同服务启动时的 Leader 选举的 接收投票步骤


统计投票

同服务启动时的 Leader 选举的 统计投票步骤


源码分析

ZooKeeper 中实现的选举算法有三种,而在目前的 ZooKeeper 3.6 版本后,只支持 “快速选举” 这一种算法。

而在代码层面的实现中,QuorumCnxManager 作为核心的实现类,用来管理 Leader 服务器与 Follow 服务器的 TCP 通信,以及消息的接收与发送等功能。在 QuorumCnxManager 中,主要定义了 ConcurrentHashMap<Long, SendWorker> 类型的 senderWorkerMap 数据字段,用来管理每一个通信的服务器。

public class QuorumCnxManager {final ConcurrentHashMap<Long, SendWorker> senderWorkerMap;final ConcurrentHashMap<Long, ArrayBlockingQueue<ByteBuffer>> queueSendMap;final ConcurrentHashMap<Long, ByteBuffer> lastMessageSent;}

而在 QuorumCnxManager 类的内部,定义了 RecvWorker 内部类。该类继承了一个 ZooKeeperThread 类的多线程类。主要负责消息接收。在 ZooKeeper 的实现中,为每一个集群中的通信服务器都分配一个 RecvWorker,负责接收来自其他服务器发送的信息。在 RecvWorker 的 run 函数中,不断通过 queueSendMap 队列读取信息。

class SendWorker extends ZooKeeperThread {Long sid;Socket sock;volatile boolean running = true;DataInputStream din;final SendWorker sw;public void run() {threadCnt.incrementAndGet();while (running && !shutdown && sock != null) {int length = din.readInt();if (length <= 0 || length > PACKETMAXSIZE) {throw new IOException("Received packet with invalid packet: "+ length);}byte[] msgArray = new byte[length];din.readFully(msgArray, 0, length);ByteBuffer message = ByteBuffer.wrap(msgArray);addToRecvQueue(new Message(message.duplicate(), sid));}}}

除了接收信息的功能外,QuorumCnxManager 内还定义了一个 SendWorker 内部类用来向集群中的其他服务器发送投票信息。如下面的代码所示。在 SendWorker 类中,不会立刻将投票信息发送到 ZooKeeper 集群中,而是将投票信息首先插入到 pollSendQueue 队列,之后通过 send 函数进行发送。

class SendWorker extends ZooKeeperThread {Long sid;Socket sock;RecvWorker recvWorker;volatile boolean running = true;DataOutputStream dout;public void run() {while (running && !shutdown && sock != null) {ByteBuffer b = null;try {ArrayBlockingQueue<ByteBuffer> bq = queueSendMap.get(sid);if (bq != null) {b = pollSendQueue(bq, 1000, TimeUnit.MILLISECONDS);} else {LOG.error("No queue of incoming messages for " +"server " + sid);break;}if(b != null){lastMessageSent.put(sid, b);send(b);}} catch (InterruptedException e) {LOG.warn("Interrupted while waiting for message on queue",e);}}}}

实现了投票信息的发送与接收后,接下来我们就来看看如何处理投票结果。

在 ZooKeeper 的底层,是通过 FastLeaderElection 类实现的。如下面的代码所示,在 FastLeaderElection 的内部,定义了最大通信间隔 maxNotificationInterval、服务器等待时间 finalizeWait 等属性配置

public class FastLeaderElection implements Election {final static int maxNotificationInterval = 60000;final static int IGNOREVALUE = -1QuorumCnxManager manager;}

在 ZooKeeper 底层通过 getVote 函数来设置本机的投票内容,如下面的代码所示,在 getVote 中通过 proposedLeader 服务器信息、proposedZxid 服务器 ZXID、proposedEpoch 投票轮次等信息封装投票信息。

synchronized public Vote getVote(){return new Vote(proposedLeader, proposedZxid, proposedEpoch);}

在完成投票信息的封装以及投票信息的接收和发送后。一个 ZooKeeper 集群中,Leader 服务器选举底层实现的关键步骤就已经介绍完了。 Leader 节点的底层实现过程的逻辑相基本分为封装投票信息、发送投票、接收投票等。


完了 走了

Apache ZooKeeper - 选举Leader源码流程深度解析相关推荐

  1. Flink从入门到精通100篇(二十四)-对Flink SQL Client 源码做深度解析

    前言 本文基于 Flink 1.12-SNAPSHOT,使用sql client命令行提交insert语句进行整个流程的分析. sql-client.sh embedded --update &quo ...

  2. 深入源码,深度解析Java 线程池的实现原理

    java 系统的运行归根到底是程序的运行,程序的运行归根到底是代码的执行,代码的执行归根到底是虚拟机的执行,虚拟机的执行其实就是操作系统的线程在执行,并且会占用一定的系统资源,如CPU.内存.磁盘.网 ...

  3. 为什么你的数据库经常会被破防呢?原因原来是这——Sql注入问题(源码+文字深度解析)

    MySQL三十二:SQL注入问题 SQL注入问题: 即sql存在漏洞,会被攻击导致数据泄露. package lesson02.utils;import java.sql.Connection; im ...

  4. (01)ORB-SLAM2源码无死角解析-(55) 闭环线程→计算Sim3:总体流程讲解ComputeSim3()

    讲解关于slam一系列文章汇总链接:史上最全slam从零开始,针对于本栏目讲解的(01)ORB-SLAM2源码无死角解析链接如下(本文内容来自计算机视觉life ORB-SLAM2 课程课件): (0 ...

  5. 【Android源码】源码分析深度好文+精编内核解析分享

    阅读Android源码的好处有很多,比如:可以加深我们对系统的了解:可以参考牛人优雅的代码实现:可以从根本上找出一些bug的原因-我们应该庆幸Android是开源的,所有的功能都可以看到实现,所有的b ...

  6. 1月28日云栖精选夜读 | 终于等到你!阿里正式向 Apache Flink 贡献 Blink 源码

    如同我们去年12月在 Flink Forward China 峰会所约,阿里巴巴内部 Flink 版本 Blink 将于 2019 年 1 月底正式开源.今天,我们终于等到了这一刻. 热点热议 终于等 ...

  7. django-admin的源码流程

    一.admin的源码流程 首先可以确定的是:路由关系一定对应一个视图函数 a.当点击运行的时候,会先找到每一个app中的admin.py文件,并执行 settings---->INSTALLED ...

  8. android 虚拟按键源码流程分析

    android 虚拟按键流程分析 今天来说说android 的虚拟按键的源码流程.大家都知道,android 系统的状态栏,虚拟按键,下拉菜单,以及通知显示,keyguard 锁屏都是在framewo ...

  9. 【 线性模型 Linear-Model 数学原理分析以及源码实现 深度学习 Pytorch笔记 B站刘二大人(1/10)】

    线性模型 Linear-Model 数学原理分析以及源码实现 深度学习 Pytorch笔记 B站刘二大人(1/10) 数学原理分析 线性模型是我们在初级数学问题中所遇到的最普遍也是最多的一类问题 在线 ...

最新文章

  1. CSS基本选择器、层次选择器、结构伪类选择器、属性选择器
  2. flask 学习笔记 mvc ,sqlalchemy(insert,update)
  3. c++ memset 语言_C++中memset函数用法详解
  4. [CSS] Scale on Hover with Transition
  5. 【最快人脸检测模型开源】libfacedetection开源
  6. 【论文阅读】PrivPy: General and Scalable Privacy-Preserving Data Mining
  7. Python学习笔记五
  8. python与分形0011 - 【教程】带辐条的多边形
  9. Log4j2 JDBCAppender的使用
  10. dis的前缀单词有哪些_前缀dis-(dif-,di-) = apart;to undo;lack of,to remove;not
  11. Android开发详解之App升级程序一点通
  12. leetcode-174-地下城游戏
  13. el-table闪动问题
  14. 马上创业网日常有话说——我与外国友人Say Hi
  15. js中some和every用法
  16. 易语言教程数据库写和添加记录
  17. 15个前端(html+css)在线网站检测工具
  18. 拓嘉辰丰电商:拼多多百万补贴是正品吗?
  19. 覃氏的由来(据历史学家考证)
  20. 红尘梦java_雪舞梅芳,只为寻红尘梦

热门文章

  1. Hha mysql_libmySQL.dll
  2. java 学习心得 (近期的)
  3. Visual C++6.0安装教程(win10版)及“应用程序无法正常启动(0x0150002)”解决办法
  4. sklearn onehot
  5. 网页拼图游戏html代码,网页设计(一):拼图游戏
  6. 安卓惯性传感器(一)
  7. 文巾解题 1310. 子数组异或查询
  8. python库整理:heapq 最小堆
  9. R语言应用实战系列(五)-朴素贝叶斯算法以及ROC和PR曲线
  10. MATLAB实战系列(三十九)-matlab多目标优化之海洋捕食者算法