浅谈Zookeeper【三】ZK中Leader选举算法

文章目录

    • 一、前言
      • 二、Leader选举基本概念
        • 集群服务节点状态 State:
        • 选票模型 Vote
        • 选举网络通信管理器 QuorumCnxManager
      • 三、Leader选举触发时机
      • 四、Leader选举过程
        • 集群初始化选举
        • Leader宕机后选举
      • 五、新节点加入与选举规则
        • 新节点加入
        • 选举规则
      • 六、选举算法FastLeaderElection解析
      • 七、总结

一、前言

前面已经介绍过Zookeeper的基础知识以及ZAB协议的一些概念,其中ZAB协议中关键性的发现步骤,也就是Leader选举机制是如何完成的,在这里简单介绍一下。

二、Leader选举基本概念

集群服务节点状态 State:
  1. LOOKING:寻找Leader节点状态,该状态下无Leader节点,触发Leader选举。
  2. FOLLOWING:跟随者状态,表明该节点为Follower。
  3. LEADING:领导状态,表明该节点为Leader节点。
  4. OBSERVING:观察者状态,表明该节点为observer节点。

关于各个节点的角色,在之前ZAB协议中已经介绍,这里不过多介绍。

选票模型 Vote

sid: ServerID表明为被推举的Leader的唯一标识,等同于MyId。

zxid: 表明被推举的Leader事务ID,也就是服务提供期间内,执行数据更新事务操作的自增ID。

electionEpoch: 逻辑时钟,表明为一次选举的周期的id,每次新的选举该值自增+1,用来判断选举投票是否为同一选举轮次。

peerEpoch: 被推举Leader节点的选举轮次Epoch。

state: 当前服务节点的状态

选举网络通信管理器 QuorumCnxManager

每台机器在启动时,都会创建一个QuorumCnxManager来负责管理节点之间的选举投票通信。
QuorumCnxManager内部维护了一系列队列用来管理待发送的消息和收到的消息以及消息的发送器,并且负责管理与其他所有节点通信连接(socket)。

QuorumCnxManager维护的队列与集合:
1、消息发送队列(queueSendMap):为其他所有节点分别维护一个消息发送队列,队列之间互不影响。
2、消息接受队列(recvQueue):一个统一的消息接收队列,负责保存接收到的消息。
3、发送器集合(senderWorkerMap): 为其他节点分配一个消息发送器,负责消息的发送。
4、最后一条发送的消息 (lastMessageSent):存储最近发送的一条消息,如果发送队列为空,则取出该消息再次发送。

QuorumCnxManager管理通信连接
为了节点之间能够互相进行选举的通信,所以所有节点之间都要两两建立连接,QuorumCnxManager创建一个ServerSocket监听3888通信端口(选举通信端口),然后持续接收其他节点的连接建立请求。
为避免连接重复建立,zk中规定只有sid较大的向sid较小的创建连接请求,当收到连接请求时,判断sid比自身小,则断开本次连接,然后自己主动发起连接建立请求。一旦连接建立,就会根据远程服务器的SID来创建相应的消息发送器SendWorker和消息接收器RecvWorker,并启动他们。

QuorumCnxManager消息发送器和消息接收器

由于创建连接时为每个远程服务器分别创建了SendWorker和RecvWorker,所以对于消息的接收,直接由RecvWorker从TCP连接中取出放入消息接收队列recvQueue即可。

对于消息的发送,只要不断的从该远程服务器的消息发送队列queueSendMap中取出一条消息发送即可,如果发送队列为空,则获取最后一次发送的信息再次发送,这里为了防止远程服务器接收消息前或接收消息后服务器挂掉后导致消息没有处理。Zookeeper能够保证接收方在处理消息时,会对重复消息进行正确的处理。

三、Leader选举触发时机

首先要确定的是,Leader选举是什么时候发起的?

  1. ZK集群启动的时刻
  2. 当出现Leader节点无法与其他节点通信的时刻

首先在ZK集群启动时还没有Leader节点出现,此时触发Leader选举。
其次在集群对外提供服务期间,如果Leader节点发生宕机或者出现网络分区,无法与其他节点进行通信,那么此时触发新一轮Leader选举。

四、Leader选举过程

根据上面提到的选举触发时机,结合下图从实例中了解选举过程。

浅谈Zookeeper【三】ZK中Leader选举算法_第1张图片

集群初始化选举

如上图所示,集群一共三个节点,在服务器初始化阶段,所有节点的zxid都为0(因为还没有处理过客户端事务请求),此时节点状态为LOOKING状态发生了Leader选举阶段。

  1. 每个节点向其他节点发送选举投票,此时因为是初始化选举,所有节点都会将自己作为Leader发送给其他节点,投票内容为[SID,ZXID]
  2. 收到其他节点发来的投票后,判断是否投票为同一轮次选举,也就是epoch是否相同,并且判断是否为LOOKING状态的服务器,如果判断不通过则不处理。
  3. 收到投票后,按照选举规则进行投票PK,规则如下:
    优先选举ZXID最大的,如果相同选举SID最大的。
  4. 如果PK结果还是自己,则跳过处理,否则将自己的选票变更为PK后的选票再次发送给其他节点。
  5. 最后统计投票,当统计出超过半数节点相同的投票,则Leader节点成功选出。
  6. 变更自己的服务器状态,如果是自己当选Leader变更为LEADING,或者是FOLLOWING。
Leader宕机后选举

也就是当出现Leader节点无法与其他节点通信的时候,此时触发新一轮的Leader选举,此时选举轮次Epoch+1,然后按照初始化流程的选举步骤一样,发起Leader选举。

与初始化选举不同的是,此时由于已经对外提供了一段时间服务,所以首先将自己的服务器状态变更为LOOKING,然后由于各个节点的Zxid不同,按照 优先选举ZXID最大的,如果相同选举SID最大的 规则进行选举。

五、新节点加入与选举规则

新节点加入

在一个新节点加入集群时,分为两种情况,一种是当前Leader已经选出,另一种是当前Leader还未选举。

  1. 当Leader节点已经选出时,新加入节点为LOOKING状态,然后发起自身的投票选举,这时其他收到选票的节点会返回给新节点Leader信息,此时新节点直接变更状态,与Leader同步即可。
  2. 当Leader节点未选出时,按照上一小节的正常选举流程参与选举即可。
选举规则

vote_sid:接收到的投票中所推举Leader服务器的SID。

vote_zxid:接收到的投票中所推举Leader服务器的ZXID。

self_sid:当前服务器自己的SID。

self_zxid:当前服务器自己的ZXID。

通过上面四个属性,可以满足如下四个规则:

  1. 在节点处理选票时,首先会判断是否为同一轮次的投票,非相同轮次不予以处理。(这里可能由于节点宕机一段时间,这段时间发生过多次Leader选举,宕机节点恢复时与当前轮次不同)
  2. 如果选举轮次相同,优先选择Zxid较大节点,这是因为保证选举出来的Leader节点具有最新最全的数据,更容易进行数据恢复同步。
  3. 如果Zxid相同,那么优先选举SID最大的节点。
  4. 按照上面的三个规则PK后,如果是自己,那么不做任何变更操作。

六、选举算法FastLeaderElection解析

之前已经介绍过QuorumCnxManager如何处理选票的通信,这里不过多介绍。 首先明确选举算法最重要的四个概念:

  1. 外部投票:接收外部其他节点选票
  2. 内部投票:自身的投票
  3. 选举轮次:zk服务器的Leader选举轮次
  4. 选票PK:通过规则来进行内部选票与外部选票的PK,决定是否变更内部选票。

FastLeaderElection算法核心流程:
整个算法流程分为10个步骤:

浅谈Zookeeper【三】ZK中Leader选举算法_第2张图片

  1. 自增选举轮次:在每一次新轮次选举时,都要自增选举轮次属性logicalclock,用来避免各个节点选举轮次不同,导致选举错误的情况。
  2. 初始化自身选票:在新一轮的选举时,初始化自身的选票Vote。
  3. 发送初始化选票:初始化选票完成后,将选票放入到发送队列sendQueue队列中,由发送器WorkerSender发送出去。
  4. 接收外部选票:每台服务器会不断的从recvqueue队列中获取外部的投票,如果服务器发现无法取得外部投票,则立即确认是否与外部服务器失去连接,如果失去连接则立即申请创建连接。
  5. 判断选举轮次:
    (1)如果外部投票选举轮次大于内部投票,则立即更新自己的轮次,并清空收到的所有选票。
    (2)外部投票小于内部投票,则直接忽略,不做处理
    (3)如果轮次相同,则进入到下一步开始选票PK
  6. 选票PK:由于之前选举投票规则已经介绍了,这里简单说下:
    (1)如果外部投票轮次大于内部投票,则变更内部投票
    (2)轮次相同,如果外部投票ZXID大于内部投票,则变更内部投票
    (3)ZXID相同,如果外部投票SID大于内部投票,则变更内部投票
  7. 变更投票:如果出现变更内部投票情况,将外部投票覆盖内部投票,然后重新发送出去。
  8. 选票归档:无论是否变更投票,都将刚刚收到的外部投票放入选票集合recvset中归档,recvset记录当前服务器在本轮次收到的所有外部投票结果,按照SID划分,例如:{(1,vote1),(2,vote2)…}
  9. 统计投票:完成选票归档后,开始统计投票,判断是否有超过半数节点认可了当前内部投票,如果有则退出选举流程,否则返回步骤4循环继续。
  10. 更新服务器状态: 当统计投票后选出了超过半数投票的节点,那么开始更新自身的服务器状态,如果内部选票就是自己,则更新为LEADING,如果不是的话,则根据情况更新为FOLLOWING或OBSERVING。

七、总结

通过如上的介绍,已经比较清晰了解了ZK的选举机制与选举算法的实现流程,比较简单的总结就是 谁Zxid大选谁,zxid相同谁服务器编号大选谁。

本文参考:
从Paxos到Zookeeper分布式一致性原理与实践。

你可能感兴趣的:(分布式与并发,算法,zookeeper)