面试官上来就问 ZAB 协议,瑟瑟发抖…
ZAB 协议是为分布式协调服务ZooKeeper专门设计的一种支持崩溃恢复的一致性协议。基于该协议,ZooKeeper 实现了一种主从模式的系统架构来保持集群中各个副本之间的数据一致性。
- 作者:老郑来源:运维开发故事|2021-04-06 06:07
Zookeeper 是通过 ZAB 一致性协议来实现分布式事务的最终一致性。
ZAB 协议介绍
ZAB 全称为 Zookeeper Atomic Broadcast(Zookeeper 原子广播协议)
ZAB 协议是为分布式协调服务ZooKeeper专门设计的一种支持崩溃恢复的一致性协议。基于该协议,ZooKeeper 实现了一种主从模式的系统架构来保持集群中各个副本之间的数据一致性。
ZAB的消息广播过程使用的是原子广播协议,类似于二阶段提交。针对客户端的请求,Leader服务器生成对应的事务提议,并将其发送给集群中所有的 Follower 服务器。然后收集各自的选票,最后进行事务提交。如图:

在 ZAB 协议中二阶段提交,移除了中断逻辑。所有的 Follower 服务器要么正常反馈 Leader 提出的事务提议,要么就抛弃 Leader 服务器。同时,我们可以在过半的 Follower 服务器已经反馈 ACK 后,就开始提交事务提议了。
Leader 服务器会为事务提议分配一个全局单调递增的 ID,称为事务 ID(ZXID)。由于 ZAB 协议需要保证每一个消息严格的因果关系,因此需要将每一个事务提议按照其 ZXID 的先后顺序进行处理。
在消息广播过程中,Leader 服务器会为每一个 Follower 服务器分配一个队列,然后将事务提议依次放入到这些队列中去,并且根据 FIFO 的策略进行消息发送。
每一个 Follower 服务器接收到这个事务提议后,会把该事务提议以事务日志的形式写入到本地磁盘中,并且写入成功后,反馈给 Leader 服务器 ACK。
当 Leader 服务器收到过半 Follower 服务器的 ACK,就发送一个 COMMIT 消息,同时 Leader 自身完成事务提交,Follower 服务器接收到 COMMIT 消息后,也进行事务提交。
之所以采用原子广播协议协议,是为了保证分布式数据一致性。过半的节点数据保存一致性。
消息广播
你可以认为消息广播机制是简化版的 2PC协议,就是通过如下的机制保证事务的顺序一致性的。

客户端提交事务请求时 Leader 节点为每一个请求生成一个事务 Proposal,将其发送给集群中所有的 Follower 节点,收到过半 Follower的反馈后开始对事务进行提交,ZAB 协议使用了原子广播协议;在 ZAB 协议中只需要得到过半的 Follower 节点反馈 Ack 就可以对事务进行提交,这也导致了 Leader 节点崩溃后可能会出现数据不一致的情况,ZAB 使用了崩溃恢复来处理数字不一致问题;消息广播使用了TCP 协议进行通讯所有保证了接受和发送事务的顺序性。广播消息时 Leader 节点为每个事务 Proposal分配一个全局递增的 ZXID(事务ID),每个事务 Proposal 都按照 ZXID 顺序来处理;
Leader 节点为每一个 Follower 节点分配一个队列按事务 ZXID 顺序放入到队列中,且根据队列的规则 FIFO 来进行事务的发送。Follower节点收到事务 Proposal 后会将该事务以事务日志方式写入到本地磁盘中,成功后反馈 Ack 消息给 Leader 节点,Leader 在接收到过半Follower 节点的 Ack 反馈后就会进行事务的提交,以此同时向所有的 Follower 节点广播 Commit 消息,Follower 节点收到 Commit 后开始对事务进行提交;
崩溃恢复
消息广播过程中,Leader 崩溃了还能保证数据一致吗?当 Leader 崩溃会进入崩溃恢复模式。其实主要是对如下两种情况的处理。
- Leader 在复制数据给所有 Follwer 之后崩溃,怎么处理?
- Leader 在收到 Ack 并提交了自己,同时发送了部分 commit 出去之后崩溃,怎么处理?
针对此问题,ZAB 定义了 2 个原则:
- ZAB 协议确保执行那些已经在 Leader 提交的事务最终会被所有服务器提交。
- ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。
至于如何实现确保提交已经被 Leader 提交的事务,同时丢弃已经被跳过的事务呢?核心是通过 ZXID 来进行处理。在崩溃过后进行恢复的时候会选择最大的 zxid 作为恢复的快照。这样的好处是: 可以省略事务提交的检查和事务的丢弃工作以提升效率
数据同步
完成Leader选举之后,在正式开始工作之前,Leader服务器会去确认事务日志中所有事务提议(指已经提交的事务提议)是否都已经被过半的机器提交了,即是否完成数据同步。下面是ZAB协议的 数据同步过程。
Leader服务器为每一个Follower服务器准备一个队列,将那些没有被Follower服务器同步的事务以事务提议的形式逐个发送给Follower服务器,并在每一个事务提议消息后面发送一个commit消息,表示该事务已被提交。
等到Follower服务器将所有其未同步的事务提议都从Leader服务器上面同步过来,并且应用到本地数据库后,Leader服务器就会将该Follower服务器加入到真正可用的Follower列表中。
ZXID 的设计
ZXID 是一个64位的数字, 如下图所示。

其中低 32 位是一个简单的单调递增的计数器,Leader 服务器产生一个新的事务提议的时候,都会对该计数器 +1。
高 32 位,用来区分不同的 Leader 服务器。具体做法是,每选举产生一个新的 Leader 服务器,就会从 Leader 服务器的本地日志中取出一个最大的 ZXID,生成对应的 epoch 值,然后再进行加1操作,之后就会以该值作为新的 epoch。并将低 32 位从 0 开始生成 ZXID。(我理解这里的 epoch 代表的就是一个 Leader 服务器的标志,每次选举 Leader 服务器,那么 epoch 值就会更新,代表是这段时期由这个新的 Leader 服务器进行事务请求的处理)。
ZAB 协议中通过 epoch 编号来区分 Leader 周期变化,能够有效避免不同 Leader 服务器使用相同的 ZXID。
下面是我 Leader 节点的 zxid 生成核心代码大家可以看一下。
- // Leader.java
- void lead() throws IOException, InterruptedException {
- // ....
- long epoch = getEpochToPropose(self.getId(), self.getAcceptedEpoch());
- zk.setZxid(ZxidUtils.makeZxid(epoch, 0));
- // ....
- }
- //
- public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws InterruptedException, IOException {
- synchronized (connectingFollowers) {
- // ....
- if (isParticipant(sid)) {
- // 将自己加入连接队伍中,方便后面判断 lead 是否有效
- connectingFollowers.add(sid);
- }
- QuorumVerifier verifier = self.getQuorumVerifier();
- // 如果有足够多的 follower 进入, 选举有效,则无需等待,并通过其他等待的线程,类似 Barrier
- if (connectingFollowers.contains(self.getId()) && verifier.containsQuorum(connectingFollowers)) {
- waitingForNewEpoch = false;
- self.setAcceptedEpoch(epoch);
- connectingFollowers.notifyAll();
- } else {
- // ....
- // followers 不够就进入等待, 超时时间为 initLimit
- while (waitingForNewEpoch && cur < end && !quitWaitForEpoch) {
- connectingFollowers.wait(end - cur);
- cur = Time.currentElapsedTime();
- }
- // 超时退出,重新选举
- if (waitingForNewEpoch) {
- throw new InterruptedException("Timeout while waiting for epoch from quorum");
- }
- }
- return epoch;
- }
- }
- // ZxidUtils
- public static long makeZxid(long epoch, long counter) {
- return (epoch << 32L) | (counter & 0xffffffffL);
- }
ZAB 协议实现
写数据的过程
下面我梳理了 zookeeper 源码中写数据的过程,如下图所示:

参考资料
https://www.cnblogs.com/veblen/p/10985676.html
https://zookeeper.apache.org
【编辑推荐】
点赞 0
- 大家都在看
- 猜你喜欢
编辑推荐
- 24H热文
- 一周话题
- 本月获赞
- 思科交换机图文设置扩展ACL的配置与应用技巧CISCO交换机如何删除 Vlan教你如何进行光纤故障判断Cisco思科提出未来智能化城市蓝图浅析Cisco AutoQoS益出思科交换机QOS限速及限制BT下载配置实例思科基础知识:虚拟局域网(VLAN)(5)思科基础知识:管理Cisco互联网络(1)
- 思科交换机图文设置扩展ACL的配置与应用技巧教你如何进行光纤故障判断CISCO交换机如何删除 Vlan思科推出Catalyst 6800核心机箱透明网桥的功能—转发和过滤如何在Cisco IOS中配置IPv6防火墙?CISCO常用配置命令及参数思科基础知识:虚拟局域网(VLAN)(2)
- 思科交换机图文设置扩展ACL的配置与应用技巧教你如何进行光纤故障判断CISCO交换机如何删除 Vlan透明网桥的功能—转发和过滤CISCO常用配置命令及参数网络命令学习基础之在cmd下更改ip地址思科基础知识:IP子网划分和变长子网掩码(2)思科推出Catalyst 6800核心机箱
订阅专栏+更多
-
数据湖与数据仓库的分析实践攻略
助力现代化数据管理:数据湖与数据仓库的分析实践攻略共3章 | 创世达人7人订阅学习
-
云原生架构实践
新技术引领移动互联网进入急速赛道共3章 | KaliArch37人订阅学习
-
数据中心和VPDN网络建设案例
漫画+案例共20章 | 捷哥CCIE231人订阅学习
视频课程+更多
-
MySQL5.7 高级性能优化
讲师:白丁20274人学习过
-
2021年软考系统规划与管理师35天特训 - 第2轮
讲师:郝工2507人学习过
-
Prometheus企业级监控系统:零基础入门
讲师:张岩峰4420人学习过
专题推荐+更多
- 精选博文
- 论坛热帖
-
订阅51CTO邮刊
点击这里查看样刊

51CTO服务号

51CTO官微