分布式算法—ZAB

1,341 阅读5分钟

ZAB 算法简介

ZAB 算法的全程是 Zookeeper 原子广播(Zookeeper atomic broadcast)算法,主要被应用在 Zookeeper 中。

算法的阶段

zab 算法主要由四个阶段组成:

  • election — 选举
  • discovery — 发现
  • synchronization — 同步
  • boardcast — 广播

算法的组成

概念

  • history:所有接受的提议
  • lastZxid:history 中所有提议最大的 zxid
  • acceptedEpoch:接受的 epoch。discovery 阶段时确认 leader
  • currentEpoch:当前的 epoch

设计的 ZAB 算法

设计的 zab 算法也会有四个阶段

election 阶段

在设计的 zab 算法中,仅仅要求在进入正式的 zab 算法时能满足这样的一个条件:在非常大的概率下,选出唯一的一个节点,这个节点是处于运行状态的,并且大多数节点同意它成为 leader。

简单来说就是希望可以选举一个大多数认可且正常运行的 leader ,不需要保证数据是否最新最全。小概率情况下,这个节点可能是异常的,或者不是大多数节点认可的节点,如果是这样的状态进入下一个节点,zab 算法会回退到 election 阶段,并重新选举。

这个阶段选举出来的 leader 叫做预主,完成 discovery 以及 synchronization 阶段且没有失败的情况下,选举出来的节点才会成为真正的 leader 节点。

处于 election 阶段的所有节点,状态都为 election 状态。在进入下一阶段时预主节点的状态会处于 leader 状态,其他节点的状态处于 follower 状态。

discovery 阶段

这个阶段的主要目的:

  1. leader 将自己的 epoch 同步给所有 follower
  2. leader 同步最新的数据为 leader 节点的历史

为了完成以上两个目的会执行以下几个过程

  1. follower 发送 Cepoch 请求给 leader
  2. leader 收到大多数 follower 发送的 Cepoch(F.acceptedEpoch) 请求。leader 生成一个新的 epoch,这个 epoch 要比收到的所有 follower 发送的 Cepoch 请求携带的 epoch 都要大。leader 发送 NEWepoch 请求,将新生成的 epoch 发送给 follower。
  3. follower 收到 leader 回复的 NEWepoch 后,会做一个比较,比较 NEWepoch 与 acceptedEpoch 的大小
    1. 如果 NEWepoch > acceptedEpoch ,修改 acceptedEpoch 为 NEWepoch ,并且回复 ACK(F.currentEpoch, F.history, F.lastZxid) 给 leader
    2. 如果 NEWepoch < acceptedEpoch , 退回到 election 阶段
  4. leader 收到所有 follower 的 ACK 请求后,按照下面的规则选出一个 follower
    1. 选择 F. currentEpoch 最大的 follower
    2. 如果 currentEpoch 相同,则选择 zxid 最大的 follower
  1. 将选出的这个 follower 的 history 同步为 leader 自己的历史

synchronization 阶段

该阶段的主要目的:

  • 实现 leader 节点与所有 follower 节点的数据同步

执行如下几个过程:

  1. leader 向所有 follower 发送 newLeader 请求,该请求会携带 leader 最新的 epoch 以及全量的历史提交(L.history)
  2. follower 收到 newLeader 请求后,先判断请求中的 epoch 是否等于 acceptedEpoch
    1. 如果不等,则退回到 election 阶段
    2. 如果相等,原子的进行同步操作
      1. 将 currentEpoch 设置为 leader 的 epoch
      2. 将 leader 的全量提交修改 epoch 后组成新的提议并存储到自己的 history。发送 ACK 给 leader
  3. leader 从大多数 follower 收到 ACK 回复后,给所有的 follower 发送 commit 请求。
  4. follower 收到 commit 后,为 history 中的每个提议调用 ADeliver()

broadcast 阶段

在广播阶段的主要目的:

  • 接收新的写请求,并同步给所有的 follower

流程如下:

  1. 当 leader 接收到一个新的请求时,增加 zxid,给所有的 follower 发送 proposal 请求
  2. follower 接收到 proposal 请求后,将提议追加到 history ,并回复 ACK 给 leader
  3. leader 接收到大多数 follower 的 ACK 后,再发送 COMMIT 请求
  4. follower 收到 COMMIT 请求后,提交提议

实现的 ZAB 算法

ZAB 1.0 算法的实现主要也是四个阶段

election 阶段

与设计的 election 阶段一致,选择出一个节点成为预主,并作为 leader 身份进入下一个阶段

discovery 阶段

该阶段在主要目的:

  1. 生成新的 epoch,并同步给所有 follower
  2. 保证 leader 的数据是最全的,否则重新选举

详细过程如下:

  1. follower 连接 leader 。并发送 followerInfo(f.acceptedEpoch) 消息
  2. 当 leader 具有大多数 follower 的连接后,停止接收连接。生成一个新的 epoch ,这个新的 epoch 大于所有的 acceptedEpoch,并发送给所有 follower
  3. 当 follower 接收到 leader 发送的 epoch,对 epoch 做一些判断:
    1. newEpoch > acceptedEpoch ,将 currentEpoch = newEpoch ,并发送 ACKEpoch(f.currentEpoch,f.lastzxid),将 follower 的 epoch 与最大事务 id 返回给 leader
    2. newEpoch = acceptedEpoch,不做任何处理。
    3. newEpoch < acceptedEpoch 。关闭 leader 连接,重新回到 election 阶段
  4. 当 leader 接收到大多数的 ACKEpoch 后,需要做以下判断:
    1. f.currentEpoch <= l.currentEpoch 。保证 leader 的 epoch 大于等于 leader 的 epoch 否则退回到 election 阶段
    2. f.currentEpoch = l.currentEpoch 则 F.lastZxid <= lastZxid。如果 epoch 相同,保证 leader 的 zxid 是最新的 zxid ,否则退回到 election 阶段

synchronization 阶段

该阶段的主要目的:

  • 完成 leader 与 follower 的同步工作

详细过程如下:

  1. leader 对连接的 follower 做如下处理:
    • 建立一个消息队列
    • 比较 follow 节点的 zxid 与 leader 的 zxid
      • 如果 follower 的 zxid 相比 leader 的 zxid 很小,也就是落后了很多数据。则将 SNAP 请求放入到队列中
      • 在 leader 的 history 中找到 epoch 为 follower 的 currentEpoch 的最大的 zxid ,如果 F.lastZxid > zxid 则说明 follower 中有未提交的提议,需要跳过。
        1. 将 TRUN(zxid) 请求加入到队列中
        2. 把大于 F.lastZxid 的所有提议加入到 DIFF 请求中,将 DIFF 请求加入到队列中
      • 将 NEWLeader 请求加入到队列中
      • 发送队列中的请求
  2. follower 接收到 SNAP/TRUN/DIFF 消息后,并不立即应用,而是等待 NEWLeader 消息,一旦收到 NEWLeader 消息,则会原子地完成如下两个操作,之后发送 ACK。
    1. 变更应用状态
    2. 设置 F.currentEpoch = e
  3. 如果 leader 接收到大多数 follower 发送的 ACK ,它取得了 epoch 为 e 的领导权。发送 UPTODATE 请求,再次开始接收 follower 的连接,设置 nextZxid = (e<<32) + 1。进入下一阶段
  4. follower 接收到 UPTODATE 请求后,进入下一阶段

注:follower 根据接收到的不同请求做不同的处理

  • 收到 NEWLeader 请求,判断 L.lastZxid,epoch < F.lastZxid,epoch 是否成立,成立则退回到 election 阶段
  • 收到 SNAP 请求,则提交 SNAP 中的所有数据
  • 收到 TRUN 请求,则删除从请求中的最大提交 zxid 到 F.lastZxid 的所有数据
  • 收到 DIFF 请求,则接收消息中携带的所有提议,并提交他们
  • 收到 UPTODATE 请求,follower 就回复 ACK(F.lastZxid)

broadcast 阶段

同设计的 ZAB 算法

设计与实现的算法差异

算法election 阶段discovery 阶段synchronization 阶段broadcast 阶段
设计的 zab 算法选择出一个预主进入一下阶段仅要求选择一个 leader ,如果 leader 的数据不是最新的,则从最新数据的 follow 同步数据修复。leader 向 follower 发送全部的 history 。通过两阶段提交,来完成提议以及提交的操作
实现的 zab 算法与设计的 election 阶段相同在选举 leader 的时候,需要保证 leader 的数据是最新最全的,通过zxid 比较来确认。leader 不发送全量的 history,而是通过对比 zxid ,发送增量的提议。与设计的广播阶段相同

ZAB 算法与其他共识算法的差异

yuque_diagram.jpg