首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >通俗易懂的讲解ZAB协议

通俗易懂的讲解ZAB协议

原创
作者头像
写bug的高哈哈
发布2025-02-03 13:58:10
发布2025-02-03 13:58:10
4040
举报

在分布式系统中,协调者是核心。如果没有协调者,机器之间的关系将退回到中心化或者指定 IP 地址调用的世界。ZAB 作为一个出色的协调者一致性协议,无论你是分布式系统的设计者还是求职者,都应该熟练掌握。

因为 ZAB 是 ZooKeeper 一致性的灵魂,为了更方便你理解 ZAB 协议,我会先带你学习一下 ZooKeeper 的基础模型,然后再看看 ZAB 协议在正常和异常情况下,分别是如何做到一致性的。

ZooKeeper 基础模型

一言以蔽之,一台 Leader 服务器负责外部客户端的写请求,其他都是 Follower 服务器。Leader 服务器将客户端的写操作数据同步到所有的 Follower 节点中。Leader 和 Follower 上的数据保持一致性,你的微服务从任何一个节点获取数据都是一致的。

很多微服务框架的注册中心都使用了 ZooKeeper,它保障了任何一个微服务在任何一台 ZooKeeper 节点看到的数据都是一致的,ZooKeeper 屏蔽了底层复杂的设计,让你轻松享用一致性带来的便捷。

正常情况下的 ZAB 协议

知道了 ZooKeeper,我们再来学习它保持一致性的秘密:ZAB 协议。

我们从字面上分析下 ZAB,它的全称是 ZooKeeper Atomic Broadcast。这里的重点是 Atomic Broadcast,也就是原子广播。

其实,我不喜欢将 ZAB 称作“协议”,因为“协议”是一个名词,就像 HTTP 协议的 header 和 body 约束,给人的感觉更像是一种格式规范,而实际上 ZAB 本质是一系列“动作”。很明显,就像 Java 并发包中的 atomic 类一样,ZAB 是不可分的一系列操作,至于广播其实就是上面提到的 Leader 向所有 Follower 传递数据的过程。

既然是原子,就代表无论集群是否正常,我都要保证广播是可以进行下去的,下面我会从正常和异常两个角度解释 ZAB 的过程。

正常的情况叫做消息广播模式,如果你之前了解过两阶段提交,理解起来就简单多了,消息广播就是一个两阶段提交过程,我们来看一下这个过程:

如图,Leader 和每个 Follower 之间会使用一个队列作为信息交换解耦,我们分步来看看这个过程:

第一步,Leader 将客户端发来的写请求发给所有的队列;

第二步,每个 Follower 从队列取出消息;

第三步,若 Follower 觉得自身可以提交这个请求变更,则 Follower 发送一个确认回复给消息队列;

第四步,Leader 从队列消费消息,如果 Leader 发现收到半数以上的确认消息,则开始第五步;

第五步,Leader 向所有的队列发送提交消息;

第六步,Follower 消费队列的消息,分别在本地提交变更。

通俗地说,就是我是老大,我要像大家传递旨意,但是传达之前我要看看大家是不是支持我,如果多数人支持我,我再决定执行。

如果不考虑异常情况,ZAB 协议我们就讲完了。

异常情况下的 ZAB 协议

以上是比较理想的情况,然而在现实中,我们不可能不考虑异常情况,我们接下来会讲述两种异常的情况,通过讲述,你就会感觉到,ZAB 的精髓其实是异常情况下的处理。

Master 挂了的情况

显然 Follower 挂了一台并不会影响什么,毕竟 Follower 只是一个副本,那么 Master 挂了怎么办呢?

这里先补充一个知识,在 ZooKeeper 中,每一个客户端写请求都有一个 id:zxid,这个 id 是由 Leader 节点的 id+ 递增的请求 id 来产生的,注意这里 id 的生成规则,它将在后面发挥大作用。

假设有 3 台机器 Server1、Server2 和 Server3,Server2 是 Leader,Server2 突然挂了,这时 Server1 和 Server3 都想当老大,于是剩下的 Server1 和 Server3 开始投票,票的格式包含服务器的 id 和前面提到的 zxid,其中服务器的 id 只是一个标识,我们假设 Server1、Server2、Server3 对应的服务器 id 分别为 1、2、3,假设 Server1 给自己投票为 (1,101899)

然后 Server3 给自己投的票是 (3,101895),这里 Server1 和 Server3 都有私心,都想自己做老大,他们各自给自己投了票并且对外广播,告诉其他节点自己的投票结果,当然这里只有 2 台活着的节点,也就是 Server1 把自己的投票结果告诉了 Server3,Server3 把自己的投票结果告诉了 Server1。

Server1 发现 Server3 的投票信息是 (3,101895),比较 zxid,发现自己的 zxid 101899 更大,所以自己才是新老大。Server3 发现 Server1 的 zxid 更大,虽然有私心但是明大义,把票改了投给 Server1,最终 Server1 获得 2 票成为了新的 Leader。

这里有同学可能会问,如果 zxid 相同怎么办呢?ZooKeeper 给出的答案是 zxid 相同的再去比较服务器 id 的大小。

至于在例子中为什么 zxid 会不相同呢,前面不是说了两阶段提交来保证原子性吗?其实由于队列的存在,本身不同节点的 zxid 就是不一样的,毕竟大家处理速度不一样嘛。此外,如果各个节点时刻保持串行一致性,那么性能表现会很糟糕。

此外单单实现了两阶段提交和原子性关系不大,只是提高了成功率。即使 Leader 收到了半数以上的确认,这个时候网络发生了故障也没办法保证请求被执行。

当然这不是说两阶段提交就没有价值了,在生产中两阶段提交还是提高了成功率。后面为了优化两阶段提交的过程还有三阶段提交等等,当然这不是我们本节课考虑的范畴,就不展开了。

ZooKeeper 集群启动

那么,除了 Master 挂了的异常情况以外,还有一个重要的场景,就是当我们启动 ZooKeeper 集群的时候,是一台一台启动的。这种情况也需要选主,假设我们启动了 Server1,它首先给自己投票 (1,101000),并且广播自己的投票数据,但此时只有 Server1 是启动状态,所以并没人收到消息。

接着 Server2 启动了,给自己投票 (2,101000),由于是初始启动所以 zxid 都一样,接着 Server2 广播了自己的投票信息 (2,101000),zxid 相同比较服务器 id,Server1 发现 Server2 更适合做 Leader,就把自己的票改了投给 Server2。最终 Server2 获得 2 票成为初始化 Leader(秦始皇)。

新的 Leader 选出来了以后,如果老的 Leader 恢复了怎么办?这时候有 2 个老大,刚恢复的 Server2 和新选出来的 Server1,体会一下 Server3 的感受,Server3 一定感觉脑子快裂开了,这种问题就是经典的脑裂问题,脑裂问题在这里是指在选主过程中,由于切换不彻底或其他原因,导致 Follower 误以为出现两个 Leader,最终使得整个集群处于混乱状态。

还记得我们说过 zxid 的组成包含 Leader 的节点 id 吗,此时请求的 zxid 已经是 Server1 的 id 了,不认 Server3 了。zxid 中的节点 id 英文叫 epoch,翻译过来是年代,我喜欢翻译成皇帝的年号,更贴切。

最后,请注意 ZooKeeper 为什么要设置奇数个结点?比如这里我们是 3 个,挂了 1 个我们还能正常工作,挂了 2 个我们就不能正常工作了,因为已经没有超过半数的节点数了,所以无法进行投票等操作了。而假设我们现在有 4 个,挂了 1 个也能工作,但是挂了 2 个也不能正常工作了,这是和 3 个一样的,而 3 个比 4 个还少 1 个,带来的效益是一样的,所以 ZooKeeper 推荐奇数个 Server。

最后新的 Leader 上任了,首先要收拾前任留下的烂摊子,如何收拾呢?这里没有标准的答案,ZAB 提供了一个标准,那就是,要确保已经被 Leader 提交的提案最终能够被所有的 Follower 提交,并且丢弃上一任 Leader 没有最终提交的请求。也就是正常情况下的消息广播过程中的第五步还未发出的请求需要丢弃。

我们再回顾一下,加深印象:

第一,投票过程先比较 zxid,zxid 相同就比较节点 id;

第二,zxid 通过年代编号来阻止脑裂问题;

第三,ZooKeeper 最好设置为奇数个节点,因为多出来的那一个没什么用处;

第四,新官上任三把火,新的 Leader 需要按照一定规则处理前任的烂摊子。

总结

今天我们学习了 ZAB 协议,其实 ZAB 协议是一系列操作的集合,我们知道了正常情况下的原子广播会进行两阶段提交,异常情况下需要考虑如何选主、新官上任后需要做什么操作,另外,“同时操作 N 个节点获取过半节点的肯定”是一个重要方法论。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ZooKeeper 基础模型
    • 正常情况下的 ZAB 协议
  • 异常情况下的 ZAB 协议
    • Master 挂了的情况
    • ZooKeeper 集群启动
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档