zookeeper选举过程详解(结合源码)
选举的意义
我们知道分布式系统CAP原则,即Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼,最多同时满足两者,即CP、AP或者CA。
- CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。从数据库上参考为传统关系型数据库,例Oracle、Mysql
- CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
- AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
需要清楚的是一个系统的CAP性不是一成不变的,也是可以在不同场景下保证不同的要求,比如在双十一、六一八这种活动期间,系统就非常需要AP而少考虑一致性原则
说了这么多就是想表明,zookeeper是一个保证CP的中间件,而Leader选举是保证分布式数据一致性的关键所在。
选举涉及到的相关概念和变量
角色:
1.LOOKING:竞选
2.OBSERVING:观察
3.FOLLOWING:跟随者
4.LEADER:领导者
投票信息:
1.logicalclock(electionEpoch):本地选举周期,每次投票都会自增
2.epoch(peerEpoch):选举届数,每次选举最终确定完leader结束选举流程时会自增(真正zxid的前32位)
3.zxid:数据ID,每次数据变动都会自增(真正zxid的后32位,zxid一共64位)
4.sid:该投票信息所属的serverId
5.leader:提议的leader(被提议的server的serverId,即sid)
投票比较规则:
1.epoch(辈分说明稳定)大的胜出,否则进行步骤2
2.zxid大的胜出,否则进行步骤3
3.sid大的胜出
选举的流程
当Zookeeper集群中的一台服务器出现以下两种情况之一时,需要进入Leader选举。
(1) 服务器初始化启动。
(2) 服务器运行期间无法和Leader保持连接。
选举过程:
首先说明,如果是一台新服务器加入到已经稳定运行、有leader的集群中,是不会产生选举流程的,直接将自己作为FOLLOWING的一员,这个也很容易理解,打个比方,在一个军队有元帅的情况下,不可能在元帅无过、并且正在领兵打仗的情况下,直接空降新的领头者。
初始化启动的选举:
- 每个Server发出一个选票,在一开始情况下一般都是投自己。
- 接受其他服务器的选票并比较选票
- 重复2过程统计投票数直至有过半机器接受到相同的投票信息,此时选出leader
- 改变服务器状态,除了此时选出leader其他都为FOLLOWING
重点讲解第二种情况的选举,根据FastLeaderElection类中的lookForLeader方法进行详解:
- 首先更新logicalclock(本地选举周期)并提议自己为leader并广播出去,头一轮都是提议自己
- 进入本轮投票的循环
- 从接收队列中获取一个投票信息,如果为空则检查是否要重发自己的投票或者重连,否则进入步骤4
- 判断投票信息中的选举状态:
LOOKING状态:
1. 如果对方的logicalclock大于本地的logicalclock,则更新本地的logicalclock并清空本地投票信息统计箱recvset,并将自己作为候选和投票中的leader进行比较,选择大的作为新的投票,然后广播出去,否则进入步骤2
2. 如果对方的logicalclock小于本地的logicalclock,则忽略对方的投票,重新进入下一轮选举流程,否则进入步骤3
3. 如果两方的logicalclock相等,则比较当前本地被推选的leader和投票中的leader,选择大的作为新的投票,然后广播出去
4. 把对方的投票信息保存到本地投票统计箱recvset中,判断当前被选举的leader是否在投票中占了大多数(大于一半的server数量),如果是则需再等待finalizeWait时间(从recvqueue继续poll投票消息)看是否有人修改了leader的候选,如果有则再将该投票信息再放回recvqueue中并重新开始下一轮循环,否则确定角色,结束选举
OBSERVING状态:没有投票权,无视直接进入下一轮选举
FOLLOWING/LEADING:这部分主要考虑两者情况,逻辑有些复杂,网上大多博客也未说清楚,笔者参考源码完善了理清了这个过程
case FOLLOWING: case LEADING: /* * Consider all notifications from the same epoch * together. */ if(n.electionEpoch == logicalclock.get()){ recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch)); if(ooePredicate(recvset, outofelection, n)) { self.setPeerState((n.leader == self.getId()) ? ServerState.LEADING: learningState()); Vote endVote = new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch); leaveInstance(endVote); return endVote; } } /* * Before joining an established ensemble, verify * a majority is following the same leader. */ outofelection.put(n.sid, new Vote(n.version, n.leader, n.zxid, n.electionEpoch, n.peerEpoch, n.state)); if(ooePredicate(outofelection, outofelection, n)) { synchronized(this){ logicalclock.set(n.electionEpoch); self.setPeerState((n.leader == self.getId()) ? ServerState.LEADING: learningState()); } Vote endVote = new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch); leaveInstance(endVote); return endVote; } break;
1.判断对方的logicalclock是否等于本地的logicalclock。不等于说明在另一个选举过程中已经有了选举结果,此时进入步骤3;等于则说明此时这个server已经投票选出结果,并且同处一个选举过程。进入步骤2
2.把对方的投票信息保存到本地投票统计箱recvset中,判断对方的投票信息是否在recvset中占大多数。不是说明对方的统计有问题,进入步骤3;是则进一步确认自己是否为leader,如果是改变自身为leader,不是则为FOLLOWING,然后结束选举
3.将对方的投票信息放入本地统计不参与投票信息箱outofelection中,判断对方的投票信息是否在outofelection中占大多数,不是进入下一轮选举;是则更新logicalclock,确认自己角色为leader还是FOLLOWING,结束选举
大多数的概念为超过1/2的参选者票数
参数文章:
理解zookeeper选举机制————https://www.cnblogs.com/shuaiandjun/p/9383655.html
zookeeper的选举机制(比较清晰)————https://blog.csdn.net/wyqwilliam/article/details/83537139
Zookeeper快速选举流程详解————https://blog.csdn.net/long290046464/article/details/81408624
CAP原则的一些思考————https://zhuanlan.zhihu.com/p/144946139?from_voters_page=true