2023-08-12
原文作者:Ressmix 原文地址:https://www.tpvlog.com/article/63

ZAB协议,全称 Zookeeper Atomic Broadcast(Zookeeper 原子广播协议)。它是专门为分布式协调服务——Zookeeper,设计的一种支持 崩溃恢复原子广播 的协议。

从设计上看,ZAB协议和 Raft 很类似。ZooKeeper集群中,只有一个Leader节点,其余均为Follower节点。整个ZAB协议一共定义了三个阶段: 发现(Discovery)同步(Synchronization)广播(Broadcast)

三个阶段执行完为一个周期,在Zookeeper集群的整个生命周期中,这三个阶段会不断进行,如果Leader崩溃或因其它原因导致Leader缺失,ZAB协议会再次进入阶段一。

一、角色

在ZooKeeper中,一共有两类节点(一个Leader节点和N个Follower节点),每个节点都会在 LookingFollowingLeading 状态间不断转换:

  1. ZooKeeper启动时(或新一轮选举时)所有节点初始状态为 Looking ,表明此时集群中没有Leader;
  2. 这时集群尝试选举出一个Leader节点,选举出的Leader节点切换为 Leading 状态;
  3. 当其它节点发现集群中已经选举出Leader,则这些节点会切换到 Following 状态,然后和Leader节点保持同步;
  4. 如果Follower节点与Leader节点失去联系,则Follower节点则会切换到 Looking 状态,开始新一轮选举。

202308122213389131.png

二、发现(Discovery)

发现(Discovery),类似于Raft中的领导者选举,该阶段会要求zookeeper集群必须选出一个Leader节点,该Leader的ZXID是全局最大的。

2.1 ZXID

ZAB协议中,使用ZXID作为事务编号,针对每个客户端的一个请求,Leader都会产生一个Proposal事务,以ZXID作为全局唯一标识:

202308122213396582.png

ZXID的低32位是一个递增的计数器,表示该事务的序号;高32位是Leader的任期编号(epoch)。每个新选举出的Leader节点,会取出本地日志中最大事务Proposal的ZXID,然后解析出对应的epoch,把该值加1作为该新Leader节点的epoch,然后将低32位重新从0开始计。

2.2 选举流程

在ZAB协议中,出现以下几种情况会开始进行Leader选举:

  • Leader节点宕机;
  • Leader节点失去了与过半Follower节点的心跳联系;
  • 集群初始化时。

Leader的选举流程如下:

  1. 每个Follower节点都向所有其它节点广播Vote投票请求,即请求自己成为Leader;
  2. 如果Follower接受到的Vote请求中的ZXID比自身的大,则投票同意,并更新自身的Vote,否则拒绝;
  3. 每个Follower都维护着一个投票记录表,当某个Follower节点收到过半的选票时,结束投票并把该Follower选为Leader。

三、同步(Synchronization)

当选举出Leader后,该Leader具有全局最大的ZXID,所以同步阶段的工作就是根据Leader的事务日志对Follower节点进行数据同步:

  1. Leader节点根据Follower节点发送过来的FOllOWERINFO请求(包含Follower节点的最大ZXID),响应NEWLEADER消息告知自己已经成为它的新Leader;

  2. Leader节点根据Follower的最大ZXID(lastZXID),向Follower发送更新指令:

    • SNAP :如果Follower的数据太老,Leader将发送快照SNAP指令给Follower同步数据;
    • DIFF :发送从Follolwer.lastZXID到Leader.lastZXID的DIFF指令给Follower同步数据;
    • TRUNC :当Follower.lastZXID比Leader.lastZXID大时,Leader发送从Leader.lastZXID到Follower.lastZXID的TRUNC指令让Follower丢弃该段数据,即回滚;
  3. Follower同步成功后回复ACKNETLEADER;

  4. 最后,Leader会把该Follower节点添加到自己的可用Follower列表中。

四、广播(Broadcast)

ZAB 协议的消息广播过程使用的是一个原子广播协议,类似二阶段提交,大体流程如下:

1.对于客户端发送的写请求,全部由 Leader 接收,如果Follower接受到则会转发给Leader。

2.Leader将请求封装成一个事务 Proposal(每个Proposal都有一个全局单调递增的ID,即ZXID),然后广播给每个 Follwer节点。

202308122213401403.png

Leader会为每个Follower节点准备一个单独的队列,事务按照ZXID大小顺序排列入队,然后根据FIFO策略进行发送。这样做是为了保证事务的顺序一致性。

3.每个Follower节点收到事务Proposal后,会先写本地日志,成功后返回一个ACK响应;Follower节点如果无法处理,则直接抛弃请求。

202308122213407614.png

4.Leader收到过半Follower节点的ACK响应后,就会广播一个Commit消息给所有Follower,通知它们进行事务的提交。与此同时,Leader节点自身也会进行事务的提交。

202308122213412495.png

5.各个Follower节点收到Commit消息后,就会完成对事务的提交。

五、总结

Leslie Lamport的 Multi-Paxos 只考虑了如何实现共识,也就是如何就一系列值达成共识,未考虑如何实现各值(也就是操作)的顺序性。最终 ZooKeeper 实现了基于Master/Slave模式的ZAB协议,保证了操作的顺序性,而且,ZAB 协议的实现,影响到了后来的共识算法,也就是 Raft 算法,Raft 除了能就一些值达成共识,还能保证各值的顺序性。

ZAB协议是通过“一切以领导者为准”的强领导者模型和严格按照顺序提交日志,来实现操作的顺序性的,这一点和 Raft 是一样的。但是ZAB 不是共识算法,不基于状态机,而是基于 Master/Slave模式 的原子广播协议,这一点是要特别注意的。

阅读全文