Zookeeper ZAB 协议

ZAB(ZooKeeper Atomic Broadcast 原子广播) 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。 在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。

原子广播

  • 在zookeeper 的主备模式下,通过 zab 协议来保证集群中各个副本数据的一致性。

  • zookeeper 使用的是单一的主进程来接收并处理所有的事务请求,并采用 zab 协议,把数据的状态变更以事务请求的形式广播到其他的节点

  • 数据更新(事务请求) 客户端每发送一个更新请求,ZooKeeper 都会生成一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序, 这个唯一编号就是事务ID(ZXID),只有更新请求才算是事务请求。

  • 为保证按照事务的 ZXID 先后顺序来处理,Leader 服务器会分别为每个 Follower 服务器创建一个队列,并按事务的先后顺序放入队列中, 并按照 FIFO 的策略进行消息发送。收到需要处理的事务后,Follower 服务器会首先将其以事务日志的形式写入服务器的磁盘中,写入成功后 会向 Leader 服务器发送 ACK 响应。

  • 当 Leader 服务器收到超过一半的 Follower 服务器的 ACK 响应后,会向所有 Follower 服务器广播 COMMIT 消息, 收到 COMMIT 消息的 Follower 服务器也会完成对事务的提交。

  • 如果接收到事务请求的是 Follower 服务器,它会将请求转发给 Leader 服务器处理。

崩溃恢复

崩溃恢复是说 Leader 节点崩溃(或者是与过半集群机器失去通信)后,会在集群中重新选举出一个 Leader 节点,并且等原来的 Leader 节点恢复后加入到集群中,仍然能保证各节点数据的一致性。

这就需要解决如下两个问题:

  • 已经被旧 Leader 提交 的 proposal 不能丢
  • 保证丢弃那些只在旧 Leader 上 提出 的 proposal

不能丢的 proposal

场景:当 Leader 节点提出一个 proposal 并发送给其他 Follower 节点,当 Leader 收到足够数量 Follower 的 ACKs 后,就向各个 Follower 广播 COMMIT 命令,同时也会在本地执行 COMMIT 并向连接的客户端返回「成功」。
但是如果在各个 Follower 在收到 COMMIT 命令前 Leader 就挂了,导致剩下的服务器并没有执行都这条消息。

如何解决以上这种情况:已经被 Leader 节点 COMMIT 的 proposal 不能丢呢?

  1. 选举拥有最大 ZXID 的 proposal 的节点作为新的 Leader。由于 Leader 上所有 proposal 被 COMMIT 之前都要有足够数量的 Follower 的 ACKs,
    即足够数量的 Follower 节点上都有这个被 COMMIT 的 proposal,因此被选举出来的新 Leader 上保存了所有被 COMMIT 的 proposal。
  2. 新的 Leader 将自己的未被提交 proposal 进行 COMMIT。
  3. 新的 Leader 和 Follower 进行先进先出队列,先将自己有但是 Follower 没有的 proposal 发送给 Follower,
    再将这些 proposal 的 COMMIT 命令广播给 Follower节点,确保所有的 Follower 处理了所有的消息。

通过如上策略可以保证已经被旧 Leader 提交的 proposal 不会丢失。

注:ZXID 是 64 位,高 32 是纪元(epoch)编号,每经过一次 Leader 选举产生一个新的 Leader,新 Leader 会将 epoch 号 +1。
低 32 位是消息计数器,每接收到一条消息这个值 +1,新 Leader 选举后这个值重置为 0。

保证丢弃的 proposal

场景:当 Leader 节点提出一个 proposal 后立即崩溃了,导致所有的 Follower 节点都没有收到这个 proposal。
当该节点从崩溃状态恢复后以 Follower 角色重连上新的 Leader,如果他依旧保留这个 proposal 的状态,与集群其他节点的状态是不一致的。所以需要丢弃。

解决方案如下:
当旧的 Leader 作为 Follower 接入新的 Leader 后,新的 Leader 会让它将所有的拥有旧的 epoch 号的未被 COMMIT 的 proposal 清除。

总结

  • Zookeeper 通过投票(半数通过则通过)选举 Leader 节点
  • Leader 节点负责接收客户端事务请求并广播处理
  • Leader 将客户端事务请求以 proposal 形式广播给 Follower 节点,并等待 ACK 响应。如果收到过半数量的 Follower 节点的 ACK 响应则向 Follower 节点广播 COMMIT 命令,Follower 节点受到 COMMIT 命令则对事务进行提交处理。
  • Leader 节点崩溃恢复后,已经被 Leader 节点 COMMIT 的 proposal 不能丢
  • Leader 节点崩溃恢复后,保证丢弃那些只在旧 Leader 上 提出 的 proposal