分布式专题——19 Zookeeper分布式一致性协议ZAB源码剖析

1 ZAB 协议

  • Zookeeper Atomic Broadcast(Zookeeper 原子广播协议):

    • ZAB 是 Paxos 算法的简化实现,并非直接采用 Paxos 来解决分布式一致性
    • 专门为分布式协调服务 Zookeeper 设计,支持崩溃恢复原子广播这两个关键能力,Zookeeper 正是基于该协议实现功能;
  • 基于 ZAB 协议,Zookeeper 采用**主备模式(Leader-Follower 模式)**的系统架构,以此保持集群中各个副本之间的数据一致性;

    • 客户端所有写入数据的操作,都先发送到 Leader 节点

    • 接着由 Leader 负责将数据复制到 Follower 节点,从而保证集群内数据的一致性;

      • 数据复制过程(类似两阶段提交 2PC,但有优化);
      • ZAB 协议在数据复制时,只需要 Follower(包含 Leader 自己的 ack)有一半以上返回 Ack 信息,就可以执行提交操作;
    • 这种优化大大减小了同步阻塞 ,同时也提高了可用性

  • ZAB 协议的两种核心模式(Zookeeper 在这两种模式间切换):

    • 消息广播模式 :当 Leader 服务可以正常使用时,Zookeeper 进入消息广播模式,进行正常的数据广播等操作;

    • 崩溃恢复模式 :当 Leader 服务不可用时,Zookeeper 进入崩溃恢复模式,以恢复集群的正常服务能力。

2 消息广播模式

  • ZAB 协议的消息广播过程采用原子广播协议 ,流程类似两阶段提交(2PC)

    • 客户端的写请求,全部由 Leader 节点接收;

    • Leader 会将请求封装成事务 Proposal ,并发送给所有 Follower 节点

    • 之后根据 Follower 的反馈,若超过半数(包含 Leader 自己)的节点成功响应 ,Leader 就会执行 commit 操作;

  • 消息广播的具体流程:

    1. Leader 收到客户端(如创建节点等)请求后:

      • 将请求封装成事务Proposal,并发送给所有 Follower;
      • 写本地数据文件 ,同时给自己发送 ack(确认)
    2. Follower 收到 Proposal 后:

      • 写本地数据文件
      • 向 Leader 返回 ack
    3. 当 Leader 收到半数以上的 ack时:

      • 向 Follower 发送 commit 指令
      • 向 Observer 发送 inform ,Observer 负责存储消息(Observer 不参与投票等关键决策,主要用于扩展读能力);
      • 执行 commit,写自己的内存数据
      • 回发节点数据变动通知给客户端 (触发客户端监听事件),最后返回客户端命令操作结果
    4. Follower 收到 commit 指令后,会写自己的内存数据执行 commit;

  • 细节补充

    • Leader 收到客户端请求后,会将请求封装成事务,并为事务分配全局递增的唯一 ID(ZXID) 。ZAB 协议需保证事务顺序,因此会按 ZXID 对事务进行先后排序后处理 ,这一排序主要通过消息队列实现;

    • Leader 和 Follower 之间存在消息队列,用于解耦二者的耦合关系,消除同步阻塞问题;

    • Zookeeper 集群为保证所有进程能有序顺序执行 ,只有 Leader 服务器能接受写请求 。若 Follower 服务器接收到客户端的写请求,会转发给 Leader 处理 ,Follower 仅能处理读请求

    • ZAB 协议规定,若一个事务在一台机器上被 commit 成功 ,那么该事务应该在所有机器上都被 commit 成功,即便有机器出现故障崩溃也不例外。

3 崩溃恢复模式

  • Leader 崩溃 (即 Leader 失去与过半 Follower 的联系)时,Zookeeper 会进入崩溃恢复模式。此时需要解决 Leader 崩溃后的数据一致性问题,比如:

    • 假设 1:Leader 在复制数据给所有 Follower 后,还没收到 Follower 的 ack 就崩溃;

    • 假设 2:Leader 收到 ack 并提交自己,且发送了部分 commit 后崩溃;

  • ZAB 协议为崩溃恢复定义的两个原则

    • 原则 1:ZAB 协议会丢弃 那些只在 Leader 提出,但没有提交的事务;

    • 原则 2:ZAB 协议确保那些已经在 Leader 提交的事务,最终会被所有服务器提交

  • ZAB 协议设计了下面这个选举算法,用于崩溃恢复时新 Leader 的选举:

    • 该选举算法要能确保提交已经被 Leader 提交的事务,同时丢弃已经被跳过的事务
    • 为满足这一要求,选举出的新 Leader 需要拥有集群中所有机器里 ZXID(事务 ID,全局递增唯一)最大的事务
    • 这样做的好处是:可以省去 Leader 服务检查事务的提交和丢弃工作这一步操作,提升恢复效率。

4 数据同步

  • 触发时机:崩溃恢复完成后,在 Leader 正式工作(接收客户端请求)之前,需要进行数据同步;

  • 目的

    • Leader 要先确认事务是否已经被过半的 Follower 提交,也就是完成数据同步,从而保持数据一致
    • 当 Follower 完成数据同步后,Leader 会将这些 Follower 加入到可用服务器列表中;
  • 实际上,Leader 服务器处理或丢弃事务都是依赖 ZXID ,那么这个 ZXID 如何生成呢?

    • ZXID 是 ZAB 协议中用于标识事务的编号,它是一个 64 位的数字,结构分为两部分;

    • 高 32 位

      • 代表了每代 Leader 的唯一性。它来源于 Leader 服务器上取出的本地日志中最大事务 Proposal 的 ZXID,并从中解析出对应的 epoch 值(Leader 选举周期)。当一轮新的选举结束后,这个值会加一,且事务 ID 会从 0 开始自增;
      • 高 32 位能让 Follower 识别不同的 Leader,简化了数据恢复流程;
    • 低 32 位

      • 代表了每代 Leader 中事务的唯一性。对于客户端的每一个事务请求,Leader 都会生成一个新的事务 Proposal,并对该计数器进行 +1 操作;

      • 低 32 位保证了每代 Leader 下事务的唯一性,结合高 32 位,可全局唯一标识事务,为事务的有序处理和数据同步等提供依据;

  • 数据同步的具体操作:当 Follower 连接上 Leader 之后,Leader 服务器会根据自己服务器上最后被提交的 ZXID和 Follower 上的 ZXID 进行比对,然后根据比对结果,要么回滚相关事务,要么和 Leader 进行同步,以确保数据一致性。

5 ZooKeeper 写数据ZAB协议源码流程图

相关推荐
间彧6 小时前
Collections.singletonList详解与应用
java
代码匠心1 天前
从零开始学Flink:数据转换的艺术
java·大数据·flink
泥嚎泥嚎3 天前
【Android】View 的滑动
java
弈宸3 天前
Transformer与ViT
算法·架构
Aurora_NeAr3 天前
Kubernetes权威指南-原理篇
后端·云原生
京茶吉鹿3 天前
三步构建完美树节点,从此告别数据结构焦虑!
java·后端
ZZHHWW3 天前
高可用架构实战指南:告别半夜被叫醒的噩梦
后端·架构
橙序员小站3 天前
搞定系统设计题:如何设计一个订单系统?
java·后端·面试
IT小番茄3 天前
Docker:macvlan实现容器跨主机通信[十四]
架构