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的一员,这个也很容易理解,打个比方,在一个军队有元帅的情况下,不可能在元帅无过、并且正在领兵打仗的情况下,直接空降新的领头者。

初始化启动的选举:

  1. 每个Server发出一个选票,在一开始情况下一般都是投自己。
  2. 接受其他服务器的选票并比较选票
  3. 重复2过程统计投票数直至有过半机器接受到相同的投票信息,此时选出leader
  4. 改变服务器状态,除了此时选出leader其他都为FOLLOWING

 

重点讲解第二种情况的选举,根据FastLeaderElection类中的lookForLeader方法进行详解:

  1. 首先更新logicalclock(本地选举周期)并提议自己为leader并广播出去,头一轮都是提议自己
  2. 进入本轮投票的循环
  3. 从接收队列中获取一个投票信息,如果为空则检查是否要重发自己的投票或者重连,否则进入步骤4
  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

posted on 2020-08-31 18:32  牧羊人李七夜  阅读(628)  评论(0编辑  收藏  举报

导航