[TOC]
Zookeeper的Leader选举
前面我们聊了一下ZAB协议以及Zookeeper的基础概念,心想着都到这个份上了,那还是把剩下的“Leader选举”、“分布式锁”、“惊群和脑裂”都跟大家简单聊聊,这些知识应该足够准备校招的你造火箭
了。
今天首先说一下Zookeeper的Leader选举流程
以及其中涉及的FastLeaderElection选举算法
。
说在前面
ZAB协议是保证Zookeeper集群数据一致性协议其中会涉及选举流程,FastLeaderElection是Zookeeper选举Leader的算法之一。这两点概念一定要搞清楚,不然很容易混为一谈。
Leader选举
两个关键时期:
- 启动Zookeeper集群时
- Leader崩溃进行崩溃恢复时
一些基础概念你需要提前预知:
1.对选举Leader的要求:
1 | 选出的leader节点上要持有最高zxid |
2.内置实现的选举算法
1 | LeaderElection |
3.选举状态
1 | LOOKING:竞选状态 |
4.部分名词
1 | 服务器id---myid(或后文的sid,集群模式下必有该配置项) |
选举流程
Zookeeper要求集群机器必须是
奇数个
(避免脑裂,下文会讲),那么我们假设有三台服务器。接着介绍一下三台服务器的Leader选举流程。
- 每个Server发出一个投票。由于是初始情况,Server1和Server2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时Server1的投票为(1, 0),Server2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。
1 | PS:不懂什么叫为自己投票(不知道票的数据结构?),别急后面带你看源码!!! |
- 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器。
- 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行比较,比较规则如下:
1 | 优先判断ZXID。ZXID(事务ID)比较大的服务器优先作为Leader。 |
对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的ZXID,均为0,再比较myid,此时Server2的myid最大,于是更新自己的投票为(2, 0),然后重新投票,对于Server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
1 | PS:于是更新自己的投票为(2, 0)? |
其涵义指的是将自己下次发出的投票信息更新为(2, 0),以该票作为新的投票依据。
- 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于Server1、Server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了Leader。
- 改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING。
简而言之
1.每个服务实例均发起选举自己为leader的投票。
2.其他服务实例收到投票邀请时,比较发起者的数据事务id是否比自己最新的事务ID大,大则给它投一票,小则不投票,相等则比较发起者的服务器ID,大则投票给它 。
3.发起者收到大家的投票反馈后,看投票数(包括自己的票数)是否大于集群的半数,大于则成为leader,未超过半数且leader未选出,则再次发起投票。
Leader选举算法
在了解了选举流程后我们介绍一下Zookeeper源码中对于算法中的实现细节。
借助网上随处可以百度到的算法描述,我再一次针对其中涉及的疑难点
做一个解说,其大致流程如下:
第一次投票。无论哪种导致进行Leader选举,集群的所有机器都处于试图选举出一个Leader的状态,即LOOKING状态,LOOKING机器会向所有其他机器发送消息,该消息称为投票。投票中包含了SID(服务器的唯一标识)和ZXID(事务ID),(SID, ZXID)形式来标识一次投票信息。
假定Zookeeper由5台机器组成,SID分别为1、2、3、4、5,ZXID分别为9、9、9、8、8,并且此时SID为2的机器是Leader机器,某一时刻,1、2所在机器出现故障,因此集群开始进行Leader选举。在第一次投票时,每台机器都会将自己作为投票对象,于是SID为3、4、5的机器投票情况分别为(3, 9),(4, 8), (5, 8)。
1 | 此时五台机器手里的投票分别为: |
变更投票。每台机器发出投票后,也会收到其他机器的投票,每台机器会根据一定规则来处理收到的其他机器的投票,并以此来决定是否需要变更自己的投票,这个规则也是整个Leader选举算法的核心所在,其中术语描述如下
1 | vote_sid:接收到的投票中所推举Leader服务器的SID。 |
每次对收到的投票的处理,都是对(vote_sid, vote_zxid)和(self_sid, self_zxid)对比的过程。
规则一:如果vote_zxid大于self_zxid,就认可当前收到的投票,并再次将该投票发送出去。(接收到的事务id大于自己当前事务id)
规则二:如果vote_zxid小于self_zxid,那么坚持自己的投票,不做任何变更。(接收到的事务id小于自己当前事务id)
规则三:如果vote_zxid等于self_zxid,那么就对比两者的SID,如果vote_sid大于self_sid,那么就认可当前收到的投票,并再次将该投票发送出去。(事务ID相等比较服务器ID及zxid)
规则四:如果vote_zxid等于self_zxid,并且vote_sid小于self_sid,那么坚持自己的投票,不做任何变更。
具体流程如图:
确定Leader。经过第二轮投票后,集群中的每台机器都会再次接收到其他机器的投票,然后开始统计投票,如果一台机器收到了超过半数的相同投票,那么这个投票对应的SID机器即为Leader。此时Server3将成为Leader。
选举流程源码
光说不练假把式,搞懂了Leader选举的基本流程,再来探究一下源码,源码之下无秘密!
用我的地址去拉取源码可能会快些。Zookeeper源码 git clone
投票数据结构
我们先解决前面的疑惑投票(或者说票)到底是什么结构?
1 | public class Vote { |
知道我们投的是什么票了,接下来我们理一下整个算法流程。
源码入口
zookeeper\zookeeper-server\src\main\java\org\apache\zookeeper\server\quorum下
非核心代码我给大家省去了,如果有兴趣想研究,可以按着我的分析流程查看源码细节。
QuorumPeerMain.java
1 | /** |
调用 QuorumPeer 的 start方法
1 |
|
执行核心选举算法
1 | // 入口前文:setCurrentVote(makeLEStrategy().lookForLeader()); |
借助一张网络图片,该图对于选举流程中涉及到数据的流向的描述还是很清楚的。
其中涉及一个网络IO管理器
:负责维护处理发送和接收两个线程。及选举算法从队列消费
和生产
投票消息。最终执行核心的选票PK
,按照一定策略进行更新和丢弃,直到选举出一个
Leader。
总结
要想理解清楚Leader选举流程,其中几个重要的概念及名词要清楚。
- 事务ID和Zxid的概念要明确
- Zxid和Sid比较的先后顺序及比较策略
- 如何理解更新选票并广播自己的选票
OK!关于Zookeeper的Leader选举流程暂时就聊这么多,后期还会对ZK实现的分布式锁以及涉及到的”惊群和脑裂的概念做一个介绍”,如果还有时间的话,再聊聊Zk是进行数据同步的几种模式!欢迎关注公众号:“Java编程之道
”!🌹
作者:爱唠嗑的阿磊
链接:https://juejin.im/post/6883483460686594061
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。