Zookeeper的选举三:Leader选举的实现细节

Leader选举的实现细节

服务器的状态

  • LOOKING:寻找Leader状态,当服务器处于该状态时,会认为当前集群中没有leader,因此需要进入leader选举流程
  • FOLLOWING:跟随者状态,当前服务器是Follower
  • LEADING:领导者状态,当前服务器为Leader
  • OBSERVING:观察者状态,当前服务器角色是Observer

Vote数据结构说明

  • id:被推举的leader的SID值
  • zxid:被推举的leader的事务ID
  • electionEpoch:用来判断多个投票是否在统一轮选举周期中。该值在服务端是一个自增序列,每次进入新一轮投票,都会对该值进行加1操作
  • peerEpoch:被推举leader的epoch
  • state:当前服务器的状态

QuorumCnxManager:网络IO

每台服务器启动的时候,都会启动一个QuorumCnxManager,负责各台服务器之间的底层leader选举工程中的网络通信。

  1. 消息队列
    QuorumCnxManager这个类内部维护了一系列的队列,用于保存接收到的、待发送的消息。
  • recvQueue:消息接收队列,用于存放那些从其他服务器接收到的消息
  • queueSendMap:消息发送队列,用于保存那些待发送的消息;它是一个Map,按SID进行分组,分别为集群中每台机器分配一个单独队列,从而保证各台机器之间的消息发送互不影响
  • senderWorkerMap:发送器集合。每个SendWorker消息发送器,都对应一台远程Zookeeper服务器,负责消息的发送。在senderWorkerMap中,也按照SID进行了分组
  • lastMessageSent:最近发送过的消息。这个集合中,为每个SID保留最近发送过的一个消息。
  1. 建立连接
    QuorumCnxManager在启动的时候,会创建一个ServerSocket来监听leader选举的通信端口(默认3888)。开启端口监听后,Zookeeper会不断的接收到来自其他服务器的“创建连接”请求,在接收到其他服务器的TCP连接请求时,会交给receiveConnection函数来处理。
    为了避免两台机器之间重复创建TCP连接,zookeeper设计了一种建立TCP连接的规则:

    只允许SID大的服务器主动和其他服务器建立连接,否则断开连接。
    服务器接收到请求后,如果发现当前服务器的SID比接收到的大,就断开连接,然后自己主动去和远程服务器建立连接

一旦建立连接,就会根据远程服务器的SID创建相应的消息发送器SendWorker和消息接收器RecvWorker,并启动他们。

  1. 消息接收和发送
    在完成选票的初始化之后,服务器就会发起第一次投票。Zookeeper会将刚刚初始化好的选票放入sendqueue队列中,由发送器WorkerSender负责发送出去

  2. 接收外部投票
    每台服务器会不断的从recvqueue队列中获取外部投票。如果服务器发现无法获取到任何的外部投票,那么就会立即确认自己是否和集群中其他服务器保持着有效连接。如果发现没有建立连接,就马上建立连接;如果已经建立了连接,那么就再次发送自己当前的内部投票。

  3. 判断选举轮次
    在处理外部投票的时候,会根据选举轮次来进行不同处理。

  • 外部选票的选举轮次大于内部选票
    当服务器发现自己的选举轮次已经落后与该外部投票对应服务器的选举轮次,那么就会立即更新自己的选举轮次(logicalclock),并且清空所有已经收到的投票,然后使用初始化的投票来进行PK以确定是否变更内部投票,最终再将内部投票发送出去
  • 外部投票的选举轮次小于内部投票
    如果接收到的选票的选举轮次落后于自己的,那么就直接忽略该外部投票
  • 外部投票的选举轮次和内部投票一样
    直接根据对比逻辑进行PK

只有在统一选举轮次的投票才是有效的

  1. 选票PK
  • 如果外部投票中被推举的leader服务器的选举轮次大于内部投票,那么就进行投票变更。小于就忽略
  • 如果投票轮次一样,就比较两者的SID。如果外部投票的ZXID大于内部投票,就进行投票变更。小于就不变
  • 如果两者ZXID一样,那么就比对两者的SID。如果外部投票的SID大于内部投票,那么就需要进行投票变更。小于就忽略
  1. 变更投票
    如果PK失败,那么就使用外部投票信息覆盖内部投票信息。变更完成后,再次将这个变更后的内部投票发送出去

  2. 选票归档
    无论是否进行了投票更新,都会将刚刚收到的那部分外部投票放入“选票集合” recvset中进行归档。
    recvset用于记录当前服务器在本轮次的leader选举中收到的所有外部投票,按照对应的SID来区分

  3. 完成了选票归档之后,就可以开始统计投票了。如果确定已经有过半的服务器认可了该内部投票,就终止投票。

    注:服务器发现有过半的服务器认可当前投票时,并不会立即更新服务器状态,而是等待一段时间(默认200毫秒)来确定是否有新的更优的投票

  4. 更新服务器状态
    终止投票后,就更新服务器状态。leader服务器更新状态为LEADING,follower服务器更新状态FOLLOWEING