共计 5791 个字符,预计需要花费 15 分钟才能阅读完成。
选举过程概述
通过后面的 zookeeper 相干的文章,咱们也对 zookeeper 有肯定的理解,晓得在 zookeeper 中存在三种服务器角色,别离是 Leader,Follower 以及 Observer,其中 Observer 仅仅作为一个监控协调者的作用,并不参加 zookeeper 对外提供服务以及 zookeeper 的选举,而 zookeeper 的选举咱们从后面的内容也晓得,总共能够分为两种,第一种是当整个 zookeeper 的集群启动后,进行的选举过程,第二种则是在 zookeeper 运行期间,当呈现 Leader 解体的过程时,zookeeper 进行的选举操作。接下来咱们先从启动的时候触发的选举开始学习
服务端启动时选举过程
咱们在搭建 zookeeper 的时候往往会有一个配置文件,外面寄存一个 myid,用来标示集群内不同客户端的机器编号,当至多两台含有 myid 的机器启动后,就开始进入了 zookeeper 的集群选举流程
1. 每个 server 发动一个投票
因为以后是刚启动状态,因而每一个服务实例都会默认把本人作为 Leader 服务器来发动投票,而每一个投票中蕴含了最根本的选举所须要的元素,例如 myid 和 ZXID,咱们将这两个依照选票的形式示意,例如 myid 为 1,ZXID 为 0,咱们将会示意为(1,0),因为每台服务端实例优先都是依照本人是 Leader 发动投票,那么 server1 默认生成的选票就是(1,0),server2 的选票则是(2,0),以此类推
2. 承受到其余服务端实例发来的选票
没台服务端实例都会收到其余服务端实例发来的选票信息,当收到选票后,就会开始校验解决选票的流程
3. 校验解决选票
其余服务实例发来的选票,要通过一系列验证,比方是不是本轮投票的选票,是否来自于 Looking 状态的实例发来的选票,通过校验后,就会和以后实例的选票信息进行 pk 比拟,比拟的规定大抵如下:
①优先比拟 ZXID,ZXID 不一样的时候,较大的那个选票所在的服务实例作为 Leader
②如果两个选票的 ZXID 雷同的话,那么就会比拟 myid,默认为 myid 较大的服务实例作为 Leader
依据这个规定,咱们来看看当 server1 收到 server2 的选票后,比拟的流程是怎么的,首先两个选票都是第一轮投票选举,所以 zxid 都是 0,接着就要开始比拟 myid 了,server1 的 myid 是 1,而 server2 的 myid 是 2,大于本身的 myid,那么 server2 就应该是 Leader,因而 server1 会更新本人的选票为(2,0),而后下次发送的时候就是发送新的选票信息进来
4. 统计每一次选票
每一次投票当前,都会统计所有的投票,判断是否有过半的实例承受到了雷同的选票信息,对与以后 server1 和 server2 来说,必须这两个实例的选票都一样才能够算是实现了选举流程,而如果是复数的实例的话,只须要达到 (实例数 + 1) / 2 的服务端实例承受到一样的选票即可。而通过下面的流程当前,只有 server1 比拟完选票,也收回了(2,0) 的选票信息,即可实现选举
5. 同步服务端实例状态
一旦选举实现,选出了 Leader 实例,每个服务实例都会更新本身的状态,如果是 Follower,就会变为 FOLLOWER,如果是 Leader 则会变成LEADING 状态。
服务端运行期间进行的选举
除了启动 zookeeper 集群的时候,个别状况下 Leader 会始终作为集群中的 Leader,即便集群中的 Follower 挂了或者是新机器实例退出集群中,也不会影响 Leader。然而一旦 Leader 无奈响应或者是宕机了,Zookeeper 集群将无奈对外进行服务,而是进行新一轮的 Leader 选举,而这个选举的过程与初始化启动集群的选举过程大体上是差不多的,然而有区别的是这个时候每个机器将要从本身的运行状态切换到选举状态
1. 更新本身状态
当 Leader 实例挂了当前,剩下的所有 Follower 实例都会将本身的服务状态变更为 LOOKING,而后进行 Leader 选举流程
2. 一样的选举流程
Leader 选举的大体流程都是一样的,这里将不再赘述,当实现选举当前,每个服务端实例依照本身的角色,将本身的状态批改为对应的角色状态,这个时候选举实现,Zookeeper 集群复原对外提供服务。
Zookeeper 的选举算法
zookeeper 的选举的大略流程咱们晓得了,然而咱们都晓得,选举的过程是基于算法的,zookeeper 的选举算法有哪些呢?在 zookeeper 中,提供了三种 Leader 选举的算法,别离是 LeaderElection、UDP 版本的 FastLeaderElection 以及 TCP 版本的 FastLeaderElection 三种选举算法。而选举算法,则是能够在 zoo.cfg 配置文件中的 electionAlg 属性来指定,这三种选举算法别离对应值为 0 -3,其中 0 为 LeaderElection 算法,应用的是 UDP 协定实现,1 代表 UDP 版本的 FastLeaderElection 算法,这种算法是非受权模式,2 代表的也是 UDP 版本的 FastLeaderElection 算法,不过这种应用的是受权模式,3 代表是 TCP 协定实现的 FastLeaderElection 算法。
不过须要留神的是,从 Zookeeper3.4.X 版本开始,Zookeeper 官网曾经废除了 UDP 协定实现的 0 - 2 这三种 Leader 选举算法,仅仅保留了 3 这一种 TCP 协定实现的 FastLeaderElection 算法,这也是为什么下面咱们介绍选举的大抵流程中不针对每一种选举算法进行剖析的起因。
Leader 选举的细节
学习了选举的大略流程当前,咱们发现整体流程和算法的设计不难,然而具体如何解决常见的问题的?这个时候咱们须要深刻细节来学习,首先 Zookeeper 为了解决不同状况,设计了多个服务端的状态,这个状态的定义在org .apache .
zookeeper . server.quorum .QuorumPeer. ServerState 类中,别离如下:
①LOOKING:寻找 Leader 服务的状态,处于以后状态后,将会进行 Leader 选举流程
②FOLLOWING:代表以后服务端处于跟随者状态,表明是 Follower 服务
③LEADING:代表以后服务端处于领导者状态,表明是 Leader 服务
④OBSERVING:观察者状态,表明是 Observer 服务
后面咱们也提到过,每次收回选票后,选票中蕴含了根本的元素,即 ZXID 和 myid,而这个选票的定义在 apache.zookeeper.server.quorum.Vote类,代码如下:
final private int version;
final private long id;
final private long zxid;
final private long electionEpoch;
final private long peerEpoch;
咱们把常见的几个属性进行阐明,如下:
属性 | 阐明 |
---|---|
id | 被选举的 SID |
zxid | 以后 Leader 的事物 ID |
electionEpoch | 逻辑时钟,解析进去的,以后处于第几轮选举投票,每次进入新一轮投票后,都会加 1 |
peerEpoch | 以后被选举的 Leader 的 epoch |
state | 以后服务所处于的状态 |
学习了这些后,咱们来看看选举的通信,后面咱们有聊过 CilentCnxn 是 Zookeeper 客户端中用于解决 I / O 网络通信的管理器,而对应的 Zookeeper 的 server 中也有一个类–QuorumCnxManager类来承受和解决 Leader 选举中的通信,而整个过程能够划分几个局部,大抵如下:
音讯队列解决音讯
在 QuorumCnxManager 类中保护了很多队列,用于保留承受到的、期待发送的音讯,还定义了音讯发送器等,除了承受队列以外,其余的队列都是依照 SID 分组的汇合。其中常见的队列和属性定义如下:
- recvQueue:音讯承受队列,用于寄存承受来的所有的音讯
- queueSendMap:音讯待发送队列,用于保留期待发送的音讯汇合,定义为一个 Map,依照 SID 分组设置为 key,并且每一个 SID 对应的都保护了一个队列,保障收发音讯互不影响
- senderWorkMap:发送器汇合,每一个 senderWork 发送器都对应一个近程连贯的 zookeeper,负责发送音讯,在 senderWorkMap 外部,也是依照 SID 分组进行保护的。
- lasteMessageSent:最近发送的音讯,在这个汇合中,会为每一个 SID 保护一个最新发送的音讯
建设连贯
为了能彼此之间通信,zookeeper 集群中的实例须要两两建设连贯,QuorumCnxManager类在启动的时候会床架一个 ServerSokect 来监听 Leader 选举的通信端口,在承受到申请的时候,会调用 receiveConnection 函数来解决,然而为了防止反复的创立 TCP 连贯,Zookeeper 建设了一个规定,只容许 SID 大的机器往 SID 小的机器建设连贯,当连贯连理后,依据近程服务实例的 SID 创立对应的 senderWorker 和对应的音讯接收器RecvWorker
音讯承受和发送
当音讯接收器不停的收到音讯后,会将其保留在 recvQueue 队列中,音讯发送比较简单,因为每一个 SID 都有一个保护的独立的 SendWorker,只须要不停的从queueSendMap 获取要发送的数据进行发送即可,发送结束后,会将刚刚发送的音讯存入 lasteMessageSent 中,然而须要留神的是,当发现带发送音讯的队列是空的时候,就会从 lasteMessageSent 中获取刚刚发送的音讯,而后再次作为音讯发送进来,这么设计的起因是为了避免接受方没有收到音讯,或者是收到音讯后挂了,导致音讯没解决完,因为 Zookeeper 本身对反复音讯有解决机制,因而反复发送音讯,能够保障能正确处理音讯
FastLeaderElection 算法
zookeeper 的选举网络 IO 模块咱们大抵晓得了,接下来咱们来看看 FastLeaderElection 选举算法的外围算法实现,流程图如下:
[外链图片转存失败, 源站可能有防盗链机制, 倡议将图片保留下来间接上传(img-cYWirecg-1634906173442)(assets/1587291882174.png)]
1. 自增选举次数
在 FastLeaderElection 的实现中,有一个 logicalclock 属性,用于标识以后选举的次数,zookeeper 要求每次发动选举的时候必须是在同一次选举周期中,因而在每一次选举之前,都会触发 logicalclock 的自增,达到以后的选举周期
2. 初始化选票
后面咱们曾经晓得了选票类的定义在apache.zookeeper.server.quorum.Vote,初始化阶段的时候,每台服务器都会推举本人为 Leader,因而都会先初始化一个以本人为主的选票
3. 将初始化的选票发送
初始化完选票当前,会将本人的选票信息存入 sendQueue 队列中,而后用对应每一个 SID 的workerSender负责发送进来
4. 承受内部投票信息
初始化阶段,除了发送本身的选票信息以外,还会承受来自其余的服务实例发来的选票信息,这些信息存入 recvQueue 队列中,如果发现无奈获取到其余选票信息,就会确认以后服务实例是否和其余的服务实例放弃着连贯,如果发现连贯断开或者是没有连贯,则会再次建设连贯,当然这里建设连贯仍然 是比照以后服务 SID 的服务发动连贯,防止出现反复创立连贯
5. 判断选举次数
当发送完初始化选票后,就会开始解决承受来的其余服务实例的选票信息,首先判断承受到的内部投票的选举次数是否大于以后的选票
- 如果大于以后服务的选票中的选举次数,那么则会更新以后服务的 logicalclock,并且清空所有收到的选票,再次拿选票和内部投票进行选票的比拟,确定是否真的要更改本身的选票,而后从新发送选票信息。
- 如果内部选票的选举次数小于以后服务实例的选举次数,那么间接忽视掉这个选票信息,并且持续发送本身的选票进来
- 如果内部选票和本身服务实例的选举次数统一,那么就须要进入选票之间的比拟操作
6. 选票的比拟
选票比拟就是整个选举算法的外围逻辑,实现在 FastLeaderElection#totalOrderPredicate 办法,选票比拟次要是为了确定以后服务实例的选票信息是否须要更改当前再次发送新的选票信息,因而会依照选举次数、ZXID 和 SID 来比拟:
- 如果内部投票的选举次数大于以后服务实例的选举次数,就须要投票变更
- 如果选举轮次统一,那么比拟 ZXID, 如果内部选票的 ZXID 比拟大,须要扭转本身服务实例的选票信息
- 如果 ZXID 统一,那么就须要比拟 SID,内部投票的 SID 较大,本身服务实例的选票信息须要扭转
7. 更改选票信息,再次发送
通过选票比拟当前,如果发现须要更改本身的选票信息,会先批改本身的选票信息,而后再次依照新的选票信息发送进来
8. 选票统计归档
每个服务实例在发送和承受过程中,无论是否清空还是忽视局部选票,都会把每一个选票的信息存入 recvSet 中进行归档统计,外部存储依照 SID 辨别选票信息,而后进行统计计算,只有发现超过半数以上的服务实例进行了投票,就能够进行发送选票信息,完结选举,否则持续反复下面的发送和承受选票过程
9. 选举 Leader 当前,进行服务实例的状态变更
当进行了投票当前,统计进去最终的 Leader 服务实例当前,就开始批改每个服务实例的状态,具体的逻辑是查看选出来的 Leader 是不是本人,如果是,则将本身的状态批改为 LEADING,如果不是,则依据状况批改本身的状态为FOLLOWING 或者是OBSERVING
至此,FastLeaderElection 算法的外围流程实现,然而咱们须要留神的是,后面 4 - 8 的步骤,可能会通过很屡次,因为每一次选举过程中,即便收到了过半的选票,不肯定就能选出 Leader 服务,可能须要选举屡次,并且每一次选举投票过程中,会有屡次发送选票的过程,因为每一个服务实例发送选票的周期是随机的,同时还须要留神的一点是,即便选票超过半数了,选出 Leader 服务实例了,也不是立即完结,而是期待 200ms,确保没有失落其余服务的更优的选票