004-zk选举相关源码分析

文章深入解析了ZooKeeper集群中的领导者选举过程,从QuorumPeerMain入口开始,详细阐述了选举算法FastLeaderElection的工作机制,包括选票格式、集群配置、网络交互、监听器逻辑以及读写操作等关键步骤,展示了ZooKeeper如何确保集群稳定性和数据一致性。
摘要由CSDN通过智能技术生成

zk源码

入口

分析 zkServer脚本会发现入口为:
ZOOMAIN=org.apache.zookeeper.server.quorum.QuorumPeerMain
所以QuorumPeerMain就是启动类
在这里插入图片描述
如果发现:org.apache.zookeeper.server.quorum.QuorumPacket 读不到
就执行下 maven compile :zookeeper-jute

集群相关

投票

选票格式 vote=(myid, ZXID)

ZXID谁大选谁
相同则看 myid谁大选谁

机器第一轮投自己然后给集群发送自己的选票
所有机器根据规则判断选举机器进入第二轮投票
投给选举规则指定的机器

在这里插入图片描述
在这里插入图片描述

读取配置文件
集群环境进入 runFromConfig(QuorumPeerConfig config)
在这里插入图片描述
这段代码种的createFactory就是没有配置就默认NIO交互
配置就是 zookeeper.serverCnxnFactory
一般会改成:

zookeeper.serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory

设置选举类型:默认值3 setElectionType
设置系统内存数据 setZkDatabase

在这里插入图片描述
startLeaderElection()
判断状态设置投票
在这里插入图片描述
这里判断自己状态为LOOKING的话就设置 currentVote为本机的mid和zxid
createElectionAlgorithm创建选举算法入参为:3
先记一下
在这里插入图片描述
其实就是创建选举相关的管理类:org.apache.zookeeper.server.quorum.QuorumCnxManager

在这里插入图片描述
启动监听器:这个监听器就是通过选举端口建立Socket连接
在new FastLeaderElection(this, qcm)中
初始化了2个队列
在这里插入图片描述
在fle.start()中设置了选票的发送和接受处理类
在这里插入图片描述
在super.start()中是要启动自身的线程看下run方法
从 Main loop开始分析
判断机器状态,此时为LOOKING且不是readonly

在这里插入图片描述
在setCueentVote中执行org.apache.zookeeper.server.quorum.FastLeaderElection#lookForLeader

更新选票内容:
在这里插入图片描述
发送选票通知:
sendNotifications();
设置了信息加入sendqueue中
在这里插入图片描述
这个队列在new FastLeaderElection(this, qcm)被初始化了然后在WorkerSender被处理

然后继续分析org.apache.zookeeper.server.quorum.FastLeaderElection#lookForLeader
在这里插入图片描述
连接所有其他机器:manager.connectAll();
在这里插入图片描述
业务逻辑在:org.apache.zookeeper.server.quorum.QuorumCnxManager#initiateConnection
建立socket连接
在这里插入图片描述
socket连接时双工的,两台机器只要建立连接就能通过链接收发消息,所以不需要重复开启两台机器之间的socket链接

Listener逻辑

org.apache.zookeeper.server.quorum.QuorumCnxManager.Listener
中run()方法中创建了org.apache.zookeeper.server.quorum.QuorumCnxManager.Listener.ListenerHandler
底层就是调用了
org.apache.zookeeper.server.quorum.QuorumCnxManager#handleConnection
在这里插入图片描述
如果sid > 接收到的sid
创建一收发Worker
在这里插入图片描述
绑定远端sid和 sw
创建一个阻塞队列queueSendMap

发送逻辑

org.apache.zookeeper.server.quorum.QuorumCnxManager#toSend
在这里插入图片描述
如果发给了自己则记录到recvQueue中
因为在发送选票的时候会拿到所有机器发送所以也会自己发给自己
如果不是自己则在queueSendMap查询之前存储的阻塞队列

接收端逻辑

org.apache.zookeeper.server.quorum.FastLeaderElection#lookForLeader
在这里插入图片描述
获取投票
不为空,判断状态
第一次n.electionEpoch == logicalclock.get()
则 totalOrderPredicate 中就会 pk 谁能胜选
在这里插入图片描述
这里就是选举逻辑
如果这个方法返回true
则说明远端的选票胜选
把票塞入投票箱后判断是否超半数 hasAllQuorums()
在这里插入图片描述
在这里插入图片描述
如果推举出来后
设置状态

        proposedLeader = leader;
        proposedZxid = zxid;
        proposedEpoch = epoch;

通知给其他服务器

继续执行
先检查是否有新的选票送过来
如果没有则
设置状态
在这里插入图片描述
如果是自己选中则修改为LEADING
否则修改为FOLLOWING
然后把主节点设置为一个选票后返回设置给 currentVote
此时在 QuorumPerr 的 run()中因为是死循环
所以会重新进入对应的状态的逻辑

选举结束之后的新增节点

分析 org.apache.zookeeper.server.quorum.FastLeaderElection.Messenger.WorkerReceiver#run

其他节点接收到新的选票

TODO 后续
如果对方是LOOKING状态,自己是非LOOKING,则不在处理接受到的选票直接通知对方自己的身份

当前新增节点

接收到选票如果自己是LOOKING
则把信息存放入:recvqueue 后 结束
然后继续
org.apache.zookeeper.server.quorum.FastLeaderElection#lookForLeader
在这里插入图片描述
在这里插入图片描述
termPredicate 重新计算选票就是判断下leader是否获取了半数以上选票,是的话返回true
checkLeader 这个只是校验下leader的状态没问题就返回 true
然后把自身改为FOLLOWING
leaveInstance 清空 recvqueue
currentVote 等于endVote

后续重新进入 org.apache.zookeeper.server.quorum.QuorumPeer#run 下一次循环

节点成为LEADING后

在这里插入图片描述
markLeader中new Socket开始使用第一个配置端口和从节点通信
在leader.lead()中启动一个新的线程org.apache.zookeeper.server.quorum.Leader.LearnerCnxAcceptor
启动这个处理LearnerCnxAcceptorHandler
并等待别的机器链接

节点为FOLLOWING

在这里插入图片描述
在follower.followLeader();中
在这里插入图片描述
和主节点建立Socket链接
之后一直在主节点同步数据(死循环)
在这里插入图片描述
其实这里也能监控到主节点是否挂掉
这里会抛异常进入catch逻辑 但是catch其实就是清理了下数据没有什么业务主要是跳出死循环
代码进入 finally逻辑
在这里插入图片描述
follower.shutdown();
关闭所有连接

在这里插入图片描述
切换本机状态为 LOOKING进入下一次投票周期

Leader挂了之后

主节点会给从节点发送ping命令
在 org.apache.zookeeper.server.quorum.Leader#lead中
在一个循环逻辑中
在这里插入图片描述
在这里插入图片描述
存入queuedPackets 队列中

流程图

1:main 1:QuorumCnxManager.Listener 1:QuorumPeer 1:FastLeaderElection.Messenger 1:LearnerCnxAcceptor 1:ContainerManager 1:LearnerHandler 脚本:zkServer.sh启动 QuorumPeerMain runFromConfig(config) 读取配置 QuorumPeer.start() currentVote=自己 electionAlg=3 QuorumCnxManager.Listener.start() fle = new FastLeaderElection(this, qcm) FastLeaderElection.Messenger.start() QuorumCnxManager.Listener.run() new ServerSocket()链接其他sid new SendWorker():queueSendMap new RecvWorker():处理Socket接收的内容 FastLeaderElection.Messenger.run() recvQueue.poll(timeout, unit) recvqueue.offer(n) sendqueue.offer(proposedLeader, proposedZxid, proposedEpoch组选票 notmsg) opt [选票的对方LOOKING] sendqueue.offer(当前选票 notmsg) opt [选票的对方LOOKING] alt [LOOKING] 如果自己非LOOKING QuorumPeer.run() reconfigFlag=false makeLEStrategy().lookForLeader() proposedLeader、proposedZxid、proposedEpoch设置为自己 sendqueue.offer(notification) n=recvqueue.poll(notTimeout,TimeUnit.MILLISECONDS) 校验选票 如果选票周期和自己相等则**totalOrderPredicate**投票pk 如果选票胜出则proposedLeader、proposedZxid、proposedEpoch设置为选票 sendqueue.offer(notification) 重新拉取所有剩余选票判断是否有其他人胜出有则退出 没有剩余选票则修改本机状态为FOLLOWING endVote = new Vote recvqueue.clear() 返回endVote opt [termPredicate 判断胜选的超半数] loop [死循环:如果状态为LOOKING] currentVote=endVote opt [LOOKING] follower=new Follower follower.followLeader() connectToLeader 找Leader要最新newEpochZxid=registerWithLeader 根据最新的zxid同步数据 读取数据正常循环或者链接不到异常跳出循环 loop [死循环] !reconfigFlag:状态改为LOOKING:结束 opt [FOLLOWING] cnxAcceptor = new LearnerCnxAcceptor() LearnerCnxAcceptor.start() newLeaderProposal.packet = new QuorumPacket(NEWLEADER startZkServer():zk.startup():ContainerManager(new LeaderRequestProcessor).start() 更新当前选票newEpoch 等待self.tickTime / 2 给所有LearnerHandler发出ping loop [死循环] opt [LEADING] loop [死循环] LearnerCnxAcceptor.run() s = new Socket new LearnerHandler(s) LearnerHandler.start() loop [死循环] LearnerHandler.run() queuedPackets.add(newLeaderQP) startSendingPackets() p = queuedPackets.poll() oa.writeRecord(p, "packet") queuedPackets.add(new QuorumPacket(Leader.UPTODATE ia.readRecord(qp, "packet") leader.processAck(this.sid, qp.getZxid(), sock.getLocalSocketAddress()) leader.zk.touch(sess, to) loop [dis.available() > 0] queuedPackets.add(qp) leader.zk.submitLearnerRequest(si) alt [qp : ACK] [qp : PING] [qp : REVALIDATE] [qp : REQUEST] loop [死循环] ContainerManager.run() lzks.checkUpgradeSession(request) nextProcessor.processRequest(upgradeRequest) 1:main 1:QuorumCnxManager.Listener 1:QuorumPeer 1:FastLeaderElection.Messenger 1:LearnerCnxAcceptor 1:ContainerManager 1:LearnerHandler
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值