暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

zookeeper - 深入源码探究zookeeper选举流程

努力努力再努力xLg 2021-05-26
426

我准备战斗到最后,不是因为我勇敢,是我想见证一切。   --双雪涛《猎人》


  • 我准备战斗到最后,不是因为我勇敢,是我想见证一切。   --双雪涛《猎人》

  • 1、源码启动集群的流程

  • 2、zookeeper的选举流程

  • 3、zookeeper选举源码分析

  • 4、启动或leader宕机选举leader流程

  • 5、zookeeper整个选举流程总结

「Thinking」


  1. 一个技术,为什么要用它,解决了那些问题?
  2. 如果不用会怎么样,有没有其它的解决方法?
  3. 对比其它的解决方案,为什么最终选择了这种,都有何利弊?
  4. 你觉得项目中还有那些地方可以用到,如果用了会带来那些问题?
  5. 这些问题你又如何去解决的呢?

「本文基于zookeeper 3.5.8」

1、源码启动集群的流程

image-20210523204833757

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

image-20210523204941347

启动第二台之后,选票过半,此时leader已经生成!一般情况下,机器2 会成为leader,第三台启动时会自动成为follower

因为在zookeeper的选举过程中是有myid
来控制的,谁大选谁。

image-20210523205749886

第一台机器会自动变为follower

2、zookeeper的选举流程

zookeeper在选举的过程中,存在固定的数据格式vote格式:[myid,ZXID]

myid:是zookeeper集群中配置

ZXID:类似于mysql中的mvcc,是一个事务id,每一次修改数据都会维护

在zookeeper的选举过程中,在投票阶段,默认是投票给ZXID大的为leader,因为ZXID大的节点一定是最新的数据。如果ZXID相同,则默认投票给myid大的节点。

zookeeper选举机制
zookeeper的选举机制

整个投票选举过程都是依赖myid 和zxid的。下面就从源码的角度看看整个选举过程

3、zookeeper选举源码分析

像zookeeper这样的中间件,启动类入口可以从启动脚本中寻找。猫一下呢!

image-20210523215118175

成功找到启动类的入口函数

4、启动或leader宕机选举leader流程

图片太大了,去我的图床上面去下载吧!
https://gitee.com/ligangyun/cloudimg/blob/master/img/20210526113705.png

5、zookeeper整个选举流程总结

在大概阅读了市面上大部分市面上的中间件后,zookeeper算是非常难看,且难懂的中间件了。写的挺抽象的0.0

在zookeeper源码中最核心的部分就是

  1. 网络通讯(server与client端的服务端口 netty/nio 监听的2181端口,其他数据同步,leader选举都是使用的BIO加上多线程实现的)
  2. 多线程
  3. 数据一致性(zookeeper 使用的ZAB 后面详细看看呢)

在整个zookeeper选举流程中,有几个比较关键的点。

  1. 选举过程中的对比:

    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

    1. 接受的投票的选票周期「大于」当前节点的选票周期
    2. 接受的投票的选票周期「等于」当前节点的选票周期,并且接受到的投票的事务ID(Zxid)「大于」当前节点的事务ID
    3. 接受到的投票的事务ID(Zxid)「等于」当前节点的事务ID并且接受到的投票的myid「大于」当前节点的myid
  2. 在zookeeper进行leader选举时,其中涉及到了多级队列,在网络通讯中使用到多级队列来解耦

    Leader选举多层队列架构

    这里有一个问题,为什么队列是与sid(myid)绑定的呢?并且都是由单独的线程去完成发送与接收的。

    与sid绑定,在多集群中通讯时,是可以完美的解耦的,避免多个节点都使用同一个队列,加入其中有节点宕机,导致其他正常服务也不能使用!

    这里吐槽一下,在zookeeper中的命名,多级队列只是一个字母大小写的区别,真的非常难看!!!!!

  3. 节点之间的socket保持长连接

    在源码中,使用了一个while循环去与leader保持长连接,并且leader会不间断的给所有的follower发送ping。一个空的数据包。

    那么在leader宕机之后,此socket在接受数据时,会抛出异常,zookeeper会在finally块里面将所有follower节点设置为LOOKING状态,进行新的选举!!!

本文仅供笔者本人学习,有错误的地方还望指出,一起进步!望海涵!

「转载请注明出处!」

欢迎关注我的公共号,无广告,不打扰。不定时更新Java后端知识,我们一起超神。

——努力努力再努力xLg

加油!


文章转载自努力努力再努力xLg,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论