zookeeper选举流程源码分析

zookeeper选举流程源码分析

选举的代码主要是在QuorumPeer.java这个类中。

它有一个内部枚举类,用来表示当前节点的状态。

   public enum ServerState {LOOKING, FOLLOWING, LEADING, OBSERVING;}

LOOKING: 当前节点在选举过程中

FOLLOWING:当前节点是从节点

LEADING: 当前节点是主节点

OBSERVING: 当前节点是观察者状态,这种状态的节点不参与选举的投票。

QuorumPeer有个run方法,就是用来根据当前节点不同的状态,进行不同的处理。

下面看下这段代码主要的框架

   @Overridepublic void run() {updateThreadName();LOG.debug("Starting quorum peer");// 这里是注册jmx消息,不用关注//下面就是选举的框架代码了try {//running 表示当前节点的状态,只要在运行过程中,就会一直根据当前节点的状态进行不同的处理while (running) {//getPeerState()用来获取当前节点的状态,就是上面提到的枚举类。//下面就会根据不同的状态进行不同的处理switch (getPeerState()) {case LOOKING:LOG.info("LOOKING");......//选举就是调用下面的这行代码来完成的。//后面我们也就单独就这个代码来进行分析setCurrentVote(makeLEStrategy().lookForLeader());......break;case OBSERVING:......//按照观察者的逻辑进行处理  break;case FOLLOWING:......//按照从节点的逻辑进行处理break;case LEADING:......//按照主节点的逻辑进行处理break;}start_fle = Time.currentElapsedTime();}} finally {......}}

上面代码的逻辑还是比较清楚的,就是一直在这几种状态之间处理。

每种状态的处理逻辑基本都是如下

                    try {//处理业务逻辑,正常情况下,会一直在这里。//除非当前的状态逻辑已经处理完毕,如LOOKING,或者抛出了异常,这时就需要重置状态} catch (Exception e) {LOG.warn("Unexpected exception",e);} finally {//重置状态updateServerState();}

下面我们看看上面选举的这行代码 setCurrentVote(makeLEStrategy().lookForLeader());。这行代码会调用具体执行选举的类执行具体的选举操作,并返回对应的投票信息,并设置成当前的投票信息。

默认的选举的是FastLeaderElection,对应的选举逻辑就在lookForLeader方法中。下面我们就直接去看看FastLeaderElectionlookForLeader方法吧。

选举的主要逻辑就是告诉其他节点。我是谁,我选谁做为主节点。

public Vote lookForLeader() throws InterruptedException {......try {//recvset用来保存投票信息,//key表示选民身份,也就是这个票是谁投的(注意:每个节点只会有一个有效的投票,后面的投票会覆盖掉之前的投票)//value用来表示具体投票的内容HashMap<Long, Vote> recvset = new HashMap<Long, Vote>();HashMap<Long, Vote> outofelection = new HashMap<Long, Vote>();int notTimeout = finalizeWait;synchronized(this){//每次投票前,会先更新这个logicalclock逻辑时钟,这个用来表示当前是第几次选举了,对比投票信息的时候会用到,这个很关键logicalclock.incrementAndGet();//首先给自己投一票updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());}LOG.info("New election. My id =  " + self.getId() +", proposed zxid=0x" + Long.toHexString(proposedZxid));//发送投票信息sendNotifications();

在上面的代码,首先会把logicalclock+1,表示当前是启动后的第几轮选取,这个参数是保存在内存中的,也就是每次启动都会从0开始。

那会不会出现节点之间logicalclock不同的情况呢,这个情况是有可能会出现的。不过后面选举过程中,相互发送消息也就会发送logicalclock,会和自己的logicalclock比较,进行修正。

在开始选举的时候,首先会给自己投一票。

会调用sendNotifications方法将投票者(自己)的信息和投票信息发出去。

会发送这些信息:

  • proposedLeader : 选举的主节点
  • proposedZxid: 选举的节点zxid,这个字段是long类型,前面32 bit表示epoch,后面32bit表示事务id
  • logicalclock:投票者的逻辑时钟
  • QuorumPeer.ServerState.LOOKING:投票者的状态(投票的状态肯定是looking)
  • sid: 投票者的id
  • proposedEpoch :选举节点的epoch,也就是proposedZxid的前面32 bit

下面看看具体的选举代码

					//如果当前节点一直是looking,且服务没有停止,就会一直进行选举流程while ((self.getPeerState() == ServerState.LOOKING) &&(!stop)){// 获取其他节点发送过来的消息Notification n = recvqueue.poll(notTimeout,TimeUnit.MILLISECONDS);//如果没有收到消息,就去检查下和其他节点的连接是否正常,尽力使消息能发送。if(n == null){......} // 验证收到消息的节点和它选举的主节点是否有效else if (validVoter(n.sid) && validVoter(n.leader)) {//这里就会根据收到消息的节点状态进行分别进行处理// 比如自己是后加入进来的,这时就已经有了leader节点,对应的也就有follow节点// 也有可能大家都刚启动,或者主节点挂掉了,这时大家都会又会是looking状态switch (n.state) {// 如果对方节点是投票状态case LOOKING:// If notification > current, replace and send messages out//首先比较logicalclock,如果对方的logicalclock比自己的大,就修正自己的`logicalclock`,同时清空自己的票箱,重新计票if (n.electionEpoch > logicalclock.get()) {logicalclock.set(n.electionEpoch);recvset.clear();// 这里会比较票的信息,如果对方选的leader节点的比自己大,就推举对方选的leader节点,否则还是将票投给自己if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,getInitId(), getInitLastLoggedZxid(), getPeerEpoch())) {updateProposal(n.leader, n.zxid, n.peerEpoch);} else {updateProposal(getInitId(),getInitLastLoggedZxid(),getPeerEpoch());}sendNotifications();// 如果自己的logicalclock 比对方的大,直接忽略对方的票} else if (n.electionEpoch < logicalclock.get()) {......break;// 如果logicalclock相等,那就直接比较自己当前选出来的leader和对方选出来的leader进行比较,如果自己的大,就不做处理,如果对方的大,就更新自己的票,重新投票} else if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,proposedLeader, proposedZxid, proposedEpoch)) {updateProposal(n.leader, n.zxid, n.peerEpoch);sendNotifications();}//在这里将对方的票扔进投票箱// don't care about the version if it's in LOOKING staterecvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));//这里就对投票进行统计了,如果过半,就要设置leader了,不过在这之前,会再等一个时间,看看其他节点是否有选出更适合的leader。//如果没有,那就设置对方节点选出来的leader为主节点,对比下leader是不是自己,如果是自己,就将自己的状态修改为leader,否则就修改成follow。同时保存当前leader信息if (termPredicate(recvset,new Vote(proposedLeader, proposedZxid,logicalclock.get(), proposedEpoch))) {// Verify if there is any change in the proposed leaderwhile((n = recvqueue.poll(finalizeWait,TimeUnit.MILLISECONDS)) != null){if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,proposedLeader, proposedZxid, proposedEpoch)){recvqueue.put(n);break;}}/** This predicate is true once we don't read any new* relevant message from the reception queue*/if (n == null) {self.setPeerState((proposedLeader == self.getId()) ?ServerState.LEADING: learningState());Vote endVote = new Vote(proposedLeader,proposedZxid, logicalclock.get(), proposedEpoch);leaveInstance(endVote);return endVote;}}break;case OBSERVING://这种状态的节点是不参与投票的,所以对它的发送的投票信息进行忽略。break;case FOLLOWING:case LEADING:
//如果对方是following或者leading,说明当前已经有主节点了,在这里就直接统计票数信息,并验证根据票数信息统计出来的leader节点和回应自己消息的自称leader节点 是不是同一个,如果是同一个,说明信息是吻合的,就会去设置自己的节点状态。需要注意的是,投票信息不但会发送给其他节点,也会给自己发送。所以这里会判断对方节点是否是当前节点。......break;default:......}} else {......}}return null;} finally {......}}

比较节点大小也比较简单。

((newEpoch > curEpoch) ||((newEpoch == curEpoch) &&((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))));

首先比较epoch,其次比较zxid,最后比较myid。

myid就是我们在zookeeper每个节点中设置myid文件中对应的值。

zxid是两部分,前32bit epoch,后32 bit 事务序号。在一个节点成为leader节点后,首先会将epoch的值+1,同时将事务序号设置成0。zxid是持久化写入文件的,所以重启也不会丢失。

logicalclock在内存中,所以每次启动都会从0开始。

给其他节点发送投票消息的时候,也会给自己发送,其他节点是通过网络发送,给自己是直接放到接收投票信息的队列。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/50048.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python Opencv实践 - 直方图显示

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR) print(img.shape)#图像直方图计算 #cv.calcHist(images, channels, mask, histSize, ranges, hist, accumulate) #images&…

C语言刷题(16)

第一题 第二题 注意 Printf&#xff08;&#xff09;可以直接在里面放字符指针进行打印 第三题 第四题 第五题 第六题 第七题 或者

edge浏览器使用jupyter notebook删除快捷键没有用?

按快捷键删除没有用&#xff0c;出现一个黑色方框&#xff0c;里面的数字不断在加 解决方法&#xff1a; 在扩展中将Global Speed控制视频速度的插件关掉&#xff0c;或者将控制速度的快捷键改一下 可以在浏览器设置 》扩展 》管理扩展 里面关掉该插件 可以在Global Speed 的…

Java文本块

现实问题&#xff1a; 在Java中&#xff0c;通常需要使用String类型表达HTML&#xff0c;XML&#xff0c;SQL或JSON等格式的字符串&#xff0c;在进行字符串赋值时需要进行转义和连接操作&#xff0c;然后才能编译该代码&#xff0c;这种表达方式难以阅读并且难以维护。 JDK1…

flink cdc初始全量速度很慢原因和优化点

link cdc初始全量速度很慢的原因之一是&#xff0c;它需要先读取所有的数据&#xff0c;然后再写入到目标端&#xff0c;这样可以保证数据的一致性和顺序。但是这样也会导致数据的延迟和资源的浪费。flink cdc初始全量速度很慢的原因之二是&#xff0c;它使用了Debezium作为捕获…

论文解读:Image-Adaptive YOLO for Object Detection in Adverse Weather Conditions

发布时间&#xff1a;2022.4.4 (2021发布&#xff0c;进过多次修订) 论文地址&#xff1a;https://arxiv.org/pdf/2112.08088.pdf 项目地址&#xff1a;https://github.com/wenyyu/Image-Adaptive-YOLO 虽然基于深度学习的目标检测方法在传统数据集上取得了很好的结果&#xf…

C++设计模式之适配器模式

一、适配器模式 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;用于将一个类的接口转换成另一个类所期望的接口&#xff0c;以便两个类能够协同工作。 适配器模式可以解决现有类接口与所需接口不匹配的问题&#xff0c;使得原本因接口不…

Mesa 23.2 开源图形栈现已可供下载

导读作为 Mesa 23 系列的第二个重要版本&#xff0c;Mesa 23.2 开源图形栈现已可供下载&#xff0c;它为 AMD GPU 的 RADV Vulkan 驱动程序带来了新功能&#xff0c;改进了 Linux 游戏&#xff0c;并新增了 Asahi 功能。 Mesa 23.2 的亮点包括 Asahi 上的 OpenGL 3.1 和 OpenGL…

《链接、装载与库》笔记

2 编译和链接 2.4 模块拼装——静态链接 链接过程主要包含了地址和空间分配(Address and Storage Allocation)、符号决议(Symbol Resolution) 和重定位&#xff08;Relocation&#xff09;等步骤。 符号决议也叫符号绑定、名称绑定、名称决议、地址绑定、指令绑定。大体意思…

Springboot 实践(8)springboot集成Oauth2.0授权包,对接spring security接口

此文之前&#xff0c;项目已经添加了数据库DAO服务接口、资源访问目录、以及数据访问的html页面&#xff0c;同时项目集成了spring security&#xff0c;并替换了登录授权页面&#xff1b;但是&#xff0c;系统用户存储代码之中&#xff0c;而且只注册了admin和user两个用户。在…

FFMPEG小白常用命令行

序列帧转H264视频 ffmpeg -r 60 -f image2 -s 1920x1080 -i fram%d.jpg -vcodec libx264 -crf 25 -pix_fmt yuv420p test.mp4 -vcodec h264 .\ffmpeg -r 60 -f image2 -s 1920x1080 -i %04d.jpeg -vcodec h264 test.mp4 %04d 表示用零来填充直到长度为4&#xff0c;i.e 000…

CGAL 求取曲线交点

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 假设 C = C 1 , C 2 , … , C n C={C_1,C_2,…,C_n}

3DXML在线查看与转换工具

3DXML 由软件巨头达索系统推出&#xff0c;是 3D 设计和工程中的关键文件格式&#xff0c;提供了封装和共享 3D 数据的系统方法。 为了将简单性与丰富的数据表示相结合&#xff0c;3DXML 在与 STEP 等其他文件格式相比时展现出其独特的优势&#xff0c;特别是在数据丰富性和紧凑…

如何在PHP中编写条件语句

引言 决策是生活不可缺少的一部分。从平凡的着装决定&#xff0c;到改变人生的工作和家庭决定。在开发中也是如此。要让程序做任何有用的事情&#xff0c;它必须能够对某种输入做出响应。当用户点击网站上的联系人按钮时&#xff0c;他们希望被带到联系人页面。如果什么都没有…

DAY24

题目一 啊 看着挺复杂 其实很简单 第一种方法 就是纵轴是怪兽编号 横轴是能力值 看看能不能打过 逻辑很简单 看看能不能打得过 打过的就在花钱和直接打里面取小的 打不过就只能花钱 这种方法就导致 如果怪兽的能力值很大 那么我们就需要很大的空间 所以引出下一种做法 纵…

GPT4模型架构的泄漏与分析

迄今为止&#xff0c;GPT4 模型是突破性的模型&#xff0c;可以免费或通过其商业门户&#xff08;供公开测试版使用&#xff09;向公众提供。它为许多企业家激发了新的项目想法和用例&#xff0c;但对参数数量和模型的保密却扼杀了所有押注于第一个 1 万亿参数模型到 100 万亿参…

视频监控平台EasyCVR视频汇聚平台档案库房图书馆等可视化管理平台应用场景全面解析

档案是一种特殊的记录留存文献&#xff0c;具有珍贵的精神财富价值。它们是人类活动的真实见证&#xff0c;是辉煌时刻的历史记录&#xff0c;在社会发展和经济建设中发挥着至关重要的作用。 随着市场经济的不断发展和人类文明的飞速推进&#xff0c;档案的价值和作用变得越来…

c与c++中struct的主要区别和c++中的struct与class的主要区别

1、c和c中struct的主要区别 c中的struct不可以含有成员函数&#xff0c;而c中的struct可以。 C语言 c中struct 是一种用于组合多个不同数据类型的数据成员的方式。struct 声明中的成员默认是公共的&#xff0c;并且不支持成员函数、访问控制和继承等概念。C中的struct通常被用…

K8S如何部署Redis(单机、集群)

在今天的讨论中&#xff0c;我们将深入研究如何将Redis数据库迁移到云端&#xff0c;以便更好地利用云计算的优势提高数据管理的灵活性。 Redis(Remote Dictionary Server)是一个开源的、基于内存的数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息代理。Redis支持多…

flutter TARGET_SDK_VERSION和android 13

config.gradle ext{SDK_VERSION 33MIN_SDK_VERSION 23TARGET_SDK_VERSION 33COMPILE_SDK_VERSION SDK_VERSIONBUILD_TOOL_VERSION "33.0.0"//兼容库版本SUPPORT_LIB_VERSION "33.0.0"}app/build.gradle里面的 defaultConfig {// TODO: Specify your…