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 阶段
这个阶段的主要目的:
- leader 将自己的 epoch 同步给所有 follower
- leader 同步最新的数据为 leader 节点的历史
为了完成以上两个目的会执行以下几个过程
- follower 发送 Cepoch 请求给 leader
- leader 收到大多数 follower 发送的 Cepoch(F.acceptedEpoch) 请求。leader 生成一个新的 epoch,这个 epoch 要比收到的所有 follower 发送的 Cepoch 请求携带的 epoch 都要大。leader 发送 NEWepoch 请求,将新生成的 epoch 发送给 follower。
- follower 收到 leader 回复的 NEWepoch 后,会做一个比较,比较 NEWepoch 与 acceptedEpoch 的大小
- 如果 NEWepoch > acceptedEpoch ,修改 acceptedEpoch 为 NEWepoch ,并且回复 ACK(F.currentEpoch, F.history, F.lastZxid) 给 leader
- 如果 NEWepoch < acceptedEpoch , 退回到 election 阶段
- leader 收到所有 follower 的 ACK 请求后,按照下面的规则选出一个 follower
- 选择 F. currentEpoch 最大的 follower
- 如果 currentEpoch 相同,则选择 zxid 最大的 follower
- 将选出的这个 follower 的 history 同步为 leader 自己的历史
synchronization 阶段
该阶段的主要目的:
- 实现 leader 节点与所有 follower 节点的数据同步
执行如下几个过程:
- leader 向所有 follower 发送 newLeader 请求,该请求会携带 leader 最新的 epoch 以及全量的历史提交(L.history)
- follower 收到 newLeader 请求后,先判断请求中的 epoch 是否等于 acceptedEpoch
- 如果不等,则退回到 election 阶段
- 如果相等,原子的进行同步操作
- 将 currentEpoch 设置为 leader 的 epoch
- 将 leader 的全量提交修改 epoch 后组成新的提议并存储到自己的 history。发送 ACK 给 leader
- leader 从大多数 follower 收到 ACK 回复后,给所有的 follower 发送 commit 请求。
- follower 收到 commit 后,为 history 中的每个提议调用 ADeliver()
broadcast 阶段
在广播阶段的主要目的:
- 接收新的写请求,并同步给所有的 follower
流程如下:
- 当 leader 接收到一个新的请求时,增加 zxid,给所有的 follower 发送 proposal 请求
- follower 接收到 proposal 请求后,将提议追加到 history ,并回复 ACK 给 leader
- leader 接收到大多数 follower 的 ACK 后,再发送 COMMIT 请求
- follower 收到 COMMIT 请求后,提交提议
实现的 ZAB 算法
ZAB 1.0 算法的实现主要也是四个阶段
election 阶段
与设计的 election 阶段一致,选择出一个节点成为预主,并作为 leader 身份进入下一个阶段
discovery 阶段
该阶段在主要目的:
- 生成新的 epoch,并同步给所有 follower
- 保证 leader 的数据是最全的,否则重新选举
详细过程如下:
- follower 连接 leader 。并发送 followerInfo(f.acceptedEpoch) 消息
- 当 leader 具有大多数 follower 的连接后,停止接收连接。生成一个新的 epoch ,这个新的 epoch 大于所有的 acceptedEpoch,并发送给所有 follower
- 当 follower 接收到 leader 发送的 epoch,对 epoch 做一些判断:
- newEpoch > acceptedEpoch ,将 currentEpoch = newEpoch ,并发送 ACKEpoch(f.currentEpoch,f.lastzxid),将 follower 的 epoch 与最大事务 id 返回给 leader
- newEpoch = acceptedEpoch,不做任何处理。
- newEpoch < acceptedEpoch 。关闭 leader 连接,重新回到 election 阶段
- 当 leader 接收到大多数的 ACKEpoch 后,需要做以下判断:
- f.currentEpoch <= l.currentEpoch 。保证 leader 的 epoch 大于等于 leader 的 epoch 否则退回到 election 阶段
- f.currentEpoch = l.currentEpoch 则 F.lastZxid <= lastZxid。如果 epoch 相同,保证 leader 的 zxid 是最新的 zxid ,否则退回到 election 阶段
synchronization 阶段
该阶段的主要目的:
- 完成 leader 与 follower 的同步工作
详细过程如下:
- leader 对连接的 follower 做如下处理:
- 建立一个消息队列
- 比较 follow 节点的 zxid 与 leader 的 zxid
- 如果 follower 的 zxid 相比 leader 的 zxid 很小,也就是落后了很多数据。则将 SNAP 请求放入到队列中
- 在 leader 的 history 中找到 epoch 为 follower 的 currentEpoch 的最大的 zxid ,如果 F.lastZxid > zxid 则说明 follower 中有未提交的提议,需要跳过。
- 将 TRUN(zxid) 请求加入到队列中
- 把大于 F.lastZxid 的所有提议加入到 DIFF 请求中,将 DIFF 请求加入到队列中
- 将 NEWLeader 请求加入到队列中
- 发送队列中的请求
- follower 接收到 SNAP/TRUN/DIFF 消息后,并不立即应用,而是等待 NEWLeader 消息,一旦收到 NEWLeader 消息,则会原子地完成如下两个操作,之后发送 ACK。
- 变更应用状态
- 设置 F.currentEpoch = e
- 如果 leader 接收到大多数 follower 发送的 ACK ,它取得了 epoch 为 e 的领导权。发送 UPTODATE 请求,再次开始接收 follower 的连接,设置 nextZxid = (e<<32) + 1。进入下一阶段
- 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 ,发送增量的提议。 | 与设计的广播阶段相同 |