写点什么

zookeeper 原理篇 -Zookeeper 选举过程分析,深入 linux 内核架构 pdf 下载

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:3468 字

    阅读完需:约 11 分钟

②如果两个选票的 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 选举的算法,分别是 LeaderElectionUDP 版本的 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,而这个选票的定义


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


apache.zookeeper.server.quorum.Vote 类,代码如下:


<pre style="margin: 0px; padding: 8px 0px 6px; box-sizing: border-box; background: rgb(241, 239, 238); border-radius: 0px; overflow-y: auto; font-size: 10px; line-height: 12px; font-family: consolas, menlo, courier, monospace, "Microsoft Yahei" !important; border-width: 1px !important; border-style: solid !important; border-color: rgb(226, 226, 226) !important;">


  1. finalprivateint version;

  2. finalprivatelong id;

  3. finalprivatelong zxid;

  4. finalprivatelong electionEpoch;

  5. finalprivatelong peerEpoch;


</pre>


我们把常见的几个属性进行说明,如下:



学习了这些后,我们来看看选举的通信,前面我们有聊过 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 选举算法的核心算法实现,流程图如下:



1.自增选举次数


FastLeaderElection 的实现中,有一个 logicalclock 属性,用于标识当前选举的次数,zookeeper 要求每次发起选举的时候必须是在同一次选举周期中,因此在每一次选举之前,都会触发 logicalclock 的自增,达到当前的选举周期


2.初始化选票


前面我们已经知道了选票类的定义在 apache.zookeeper.server.quorum.Vote,初始化阶段的时候,每台服务器都会推举自己为 Leader,因此都会先初始化一个以自己为主的选票


3.将初始化的选票发送


初始化完选票以后,会将自己的选票信息存入 sendQueue 队列中,然后用对应每一个 SID workerSender 负责发送出去


4.接受外部投票信息


初始化阶段,除了发送自身的选票信息以外,还会接受来自其他的服务实例发来的选票信息,这些信息存入 recvQueue 队列中,如果发现无法获取到其他选票信息,就会确认当前服务实例是否和其他的服务实例保持着连接,如果发现连接断开或者是没有连接,则会再次建立连接,当然这里建立连接依然是对比当前服务 SID 的服务发起连接,防止出现重复创建连接


5.判断选举次数


当发送完初始化选票后,就会开始处理接受来的其他服务实例的选票信息,首先判断接受到的外部投票的选举次数是否大于当前的选票


  • 如果大于当前服务的选票中的选举次数,那么则会更新当前服务的 logicalclock,并且清空所有收到的选票,再次拿选票和外部投票进行选票的比较,确定是否真的要更改自身的选票,然后重新发送选票信息。

  • 如果外部选票的选举次数小于当前服务实例的选举次数,那么直接无视掉这个选票信息,并且继续发送自身的选票出去

  • 如果外部选票和自身服务实例的选举次数一致,那么就需要进入选票之间的比较操作


6.选票的比较

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
zookeeper原理篇-Zookeeper选举过程分析,深入linux内核架构pdf下载