④OBSERVING:观察者状态,表明是Observer服务

前面我们也提到过,每次发出选票后,选票中包含了基本的元素,即ZXID和myid,而这个选票的定义在 apache.zookeeper.server.quorum.Vote类,代码如下:

1. `finalprivateint version;`

2. `finalprivatelong id;`

3. `finalprivatelong zxid;`

4. `finalprivatelong electionEpoch;`

5. `finalprivatelong peerEpoch;`

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

属性说明id被选举的SIDzxid当前Leader的事物IDelectionEpoch逻辑时钟,解析出来的,当前处于第几轮选举投票,每次进入新一轮投票后,都会加1peerEpoch当前被选举的Leader的epochstate当前服务所处于的状态

学习了这些后,我们来看看选举的通信,前面我们有聊过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.选票的比较

选票比较就是整个选举算法的核心逻辑,实现在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,确保没有丢失其他服务的更优的选票

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)

最后

由于文案过于长,在此就不一一介绍了,这份Java后端架构进阶笔记内容包括:Java集合,JVM、Java并发、微服务、SpringNetty与 RPC 、网络、日志 、Zookeeper 、Kafka 、RabbitMQ 、Hbase 、MongoDB、Cassandra 、Java基础、负载均衡、数据库、一致性算法、Java算法、数据结构、分布式缓存等等知识详解。

本知识体系适合于所有Java程序员学习,关于以上目录中的知识点都有详细的讲解及介绍,掌握该知识点的所有内容对你会有一个质的提升,其中也总结了很多面试过程中遇到的题目以及有对应的视频解析总结。

的所有内容对你会有一个质的提升,其中也总结了很多面试过程中遇到的题目以及有对应的视频解析总结。

[外链图片转存中…(img-AsUojouM-1711985254903)]

[外链图片转存中…(img-344co8XZ-1711985254904)]

参考阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。