我准备战斗到最后,不是因为我勇敢,是我想见证一切。 --双雪涛《猎人》
我准备战斗到最后,不是因为我勇敢,是我想见证一切。 --双雪涛《猎人》
1、源码启动集群的流程
2、zookeeper的选举流程
3、zookeeper选举源码分析
4、启动或leader宕机选举leader流程
5、zookeeper整个选举流程总结
❝❞
一个技术,为什么要用它,解决了那些问题? 如果不用会怎么样,有没有其它的解决方法? 对比其它的解决方案,为什么最终选择了这种,都有何利弊? 你觉得项目中还有那些地方可以用到,如果用了会带来那些问题? 这些问题你又如何去解决的呢?
「本文基于zookeeper 3.5.8」
1、源码启动集群的流程

❝启动一台机器时,会进行选举,会报错!!!
❞

启动第二台之后,选票过半,此时leader已经生成!一般情况下,机器2 会成为leader,第三台启动时会自动成为follower
❝因为在zookeeper的选举过程中是有
❞myid
来控制的,谁大选谁。

第一台机器会自动变为follower
2、zookeeper的选举流程
zookeeper在选举的过程中,存在固定的数据格式vote格式:[myid,ZXID]
❝myid:是zookeeper集群中配置
ZXID:类似于mysql中的mvcc,是一个事务id,每一次修改数据都会维护
❞
在zookeeper的选举过程中,在投票阶段,默认是投票给ZXID大的为leader,因为ZXID大的节点一定是最新的数据。如果ZXID相同,则默认投票给myid大的节点。


❝整个投票选举过程都是依赖myid 和zxid的。下面就从源码的角度看看整个选举过程
❞
3、zookeeper选举源码分析
像zookeeper这样的中间件,启动类入口可以从启动脚本中寻找。猫一下呢!

❝成功找到启动类的入口函数
❞
4、启动或leader宕机选举leader流程
5、zookeeper整个选举流程总结
在大概阅读了市面上大部分市面上的中间件后,zookeeper算是非常难看,且难懂的中间件了。写的挺抽象的0.0
❝在zookeeper源码中最核心的部分就是
❞
网络通讯(server与client端的服务端口 netty/nio 监听的2181端口,其他数据同步,leader选举都是使用的BIO加上多线程实现的) 多线程 数据一致性(zookeeper 使用的ZAB 后面详细看看呢)
在整个zookeeper选举流程中,有几个比较关键的点。
选举过程中的对比:
zookeeper的选举流程中会维护一个Vote数据结构

该结构用于参与PK选票时起到决定性的作用。整个PK选票的核心逻辑如下
protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) {
LOG.debug("id: " + newId + ", proposed id: " + curId + ", zxid: 0x" +
Long.toHexString(newZxid) + ", proposed zxid: 0x" + Long.toHexString(curZxid));
if(self.getQuorumVerifier().getWeight(newId) == 0){
return false;
}
/*
* We return true if one of the following three cases hold:
* 1- New epoch is higher
* 2- New epoch is the same as current epoch, but new zxid is higher
* 3- New epoch is the same as current epoch, new zxid is the same
* as current zxid, but server id is higher.
*/
return ((newEpoch > curEpoch) ||
((newEpoch == curEpoch) &&
((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))));
}只有以上三种情况会返回true
❝
以上三种情况,只要有一个成立,那么接受到的投票与当前节点PK成功,当前节点推荐投票过来的节点为
❞leader接受的投票的选票周期「大于」当前节点的选票周期 接受的投票的选票周期「等于」当前节点的选票周期,并且接受到的投票的事务ID(Zxid)「大于」当前节点的事务ID 接受到的投票的事务ID(Zxid)「等于」当前节点的事务ID并且接受到的投票的myid「大于」当前节点的myid 在zookeeper进行leader选举时,其中涉及到了多级队列,在网络通讯中使用到多级队列来解耦

Leader选举多层队列架构 这里有一个问题,为什么队列是与sid(myid)绑定的呢?并且都是由单独的线程去完成发送与接收的。
❝
与sid绑定,在多集群中通讯时,是可以完美的解耦的,避免多个节点都使用同一个队列,加入其中有节点宕机,导致其他正常服务也不能使用!
这里吐槽一下,在zookeeper中的命名,多级队列只是一个字母大小写的区别,真的非常难看!!!!!
❞节点之间的socket保持长连接
在源码中,使用了一个while循环去与leader保持长连接,并且leader会不间断的给所有的follower发送ping。一个空的数据包。
那么在leader宕机之后,此socket在接受数据时,会抛出异常,zookeeper会在finally块里面将所有follower节点设置为LOOKING状态,进行新的选举!!!
❝本文仅供笔者本人学习,有错误的地方还望指出,一起进步!望海涵!
「转载请注明出处!」
欢迎关注我的公共号,无广告,不打扰。不定时更新Java后端知识,我们一起超神。
——努力努力再努力xLg
加油!
❞





