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
其他节点接收到新的选票
如果对方是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 队列中