Zookeeper详解

136 阅读7分钟

Zookeeper 详解

核心定位:分布式协调服务
提供高可用、强一致性的核心基础设施,用于解决分布式系统中的协调难题:

  • 服务注册与发现:服务动态上下线通知(如 Dubbo)
  • 分布式锁:实现互斥访问共享资源
  • 集群选主(Leader Election):确定主节点处理关键任务
  • 配置管理:集中存储并动态推送配置
  • 命名服务:维护全局唯一路径名
  • 集群管理:监控节点状态

核心协议:ZAB (ZooKeeper Atomic Broadcast) 协议
保证分布式系统数据一致性的基石,包含两个核心阶段:

  1. Leader 选举:集群启动或 Leader 故障时,快速选出新 Leader(基于 epoch, zxid, sid 比较)。
  2. 原子广播(消息广播)
    • Leader 接收写请求,生成唯一递增事务 ID (zxid)。
    • Leader 将带 zxid 的提案 (Proposal) 广播给所有 Follower。
    • Follower 将提案持久化到事务日志,成功后返回 ACK
    • Leader 收到过半 Follower 的 ACK 后,发送 COMMIT 命令。
    • Follower 收到 COMMIT 后,将提案应用到内存数据库 (DataTree)。

核心特性:

  • 顺序一致性:客户端请求按发送顺序执行。
  • 原子性:事务要么全成功要么全失败。
  • 单一系统映像:客户端无论连接哪个节点,看到相同数据视图。
  • 可靠性:事务一旦提交,结果永久保存直到被覆盖。
  • 最终一致性:客户端最终能看到最新提交的数据(读 Follower 可能有短暂延迟)。
  • 高性能:读操作负载均衡到所有节点,极高吞吐;写操作由 Leader 处理,依赖网络和持久化速度。

选举机制详解 (面试重点)

核心目标:快速、公平选出 Leader,防止脑裂。

1. 首次启动选举 (以节点 sid=1,2,3 为例)

  • 节点1启动:
    • 状态:LOOKING (寻找 Leader)。
    • 投票:投给自己 (sid=1, zxid=0, epoch=0)
    • 结果:票数(1) < 半数(2),无 Leader。
  • 节点2启动:
    • 状态:LOOKING
    • 投票:投给自己 (sid=2, zxid=0, epoch=0)
    • 通信: 节点2 向节点1 发送投票。节点1 发现节点2 的 sid(2) > 自己的 sid(1)更新投票为投给节点2 (sid=2, zxid=0, epoch=0) 并告知节点2。
    • 节点2 统计:收到自己的票 + 节点1 的票 = 2票 >= 半数(2)。
    • 结果:节点2 当选 Leader (状态变 LEADING)。 节点1 成为 Follower (状态变 FOLLOWING)。
  • 节点3启动:
    • 状态:LOOKING
    • 投票:投给自己 (sid=3, zxid=0, epoch=0)
    • 通信:向已存在的节点(1或2)询问。得知已有 Leader (节点2)。
    • 结果:节点3 直接成为 Follower (FOLLOWING)。

2. 运行期 Leader 故障选举 (核心三要素比较)

剩余 Follower 进入 LOOKING 状态发起投票。投票比较优先级(依次判断):

  1. Epoch (逻辑时钟): 最新选举轮次编号。epoch 大的优先。 (防止旧 Leader 的提案干扰新选举)
  2. Zxid (事务ID): 节点最后提交的事务 ID。zxid 大的优先。 (保证数据最新的节点优先)
  3. Sid (服务器ID): 配置文件中 myid 的值。sid 大的优先。 (唯一标识,保证选举结果唯一)

选举过程:

  • 每个节点初始投票投给自己 (my_epoch, my_zxid, my_sid)
  • 节点交换投票信息。
  • 收到他人投票时,按上述优先级比较:
    • 若对方的投票“更优”(epoch 更大,或 epoch 相等时 zxid 更大,或 epochzxid 都相等时 sid 更大),则更新自己的投票为投给这个更优的节点。
    • 否则,坚持自己当前的投票。
  • 统计投票:当某个节点发现自己收到的投票中有过半票指向同一个节点(可能是自己或别人),则该节点当选 Leader。广播结果,其他节点成为 Follower。

3. 如何防止脑裂 (Split-Brain)

Zookeeper 通过 “过半机制” (Quorum) 从根本上防止脑裂:

  • 写入要求过半: Leader 必须收到超过半数 Follower 的 ACK 才能提交事务 (COMMIT)。
  • 选举要求过半: 一个节点必须获得超过半数的投票才能当选 Leader。
  • 网络分区后果:
    • 如果一个分区拥有超过半数的节点,这个分区能正常选举新 Leader 并继续服务。
    • 如果一个分区节点数不足半数,该分区无法选举出 Leader,也无法完成写请求(因为无法获得过半 ACK)。
  • 结果: 整个集群中最多只有一个分区能正常工作(拥有过半节点的分区),其他分区处于不可用状态。保证了集群中最多只有一个有效的 Leader,避免脑裂导致的数据不一致。

节点类型 (ZNode Types)

Zookeeper 的数据模型是树形结构(类似文件系统),每个节点称为 ZNode。节点类型决定了其生命周期和命名规则:

  1. 持久节点 (Persistent Node) - PERSISTENT

    • 特点: 创建后一直存在,除非显式删除 (delete)。与客户端会话无关。
    • 命令: create /path data
    • 用途: 存储需要长期存在的配置信息、元数据等。
  2. 持久顺序节点 (Persistent Sequential Node) - PERSISTENT_SEQUENTIAL

    • 特点: 持久节点 + 创建时 ZK 自动在路径名后缀一个单调递增的序号 (10位数字,如 /path0000000001)。与客户端会话无关。
    • 命令: create -s /path data
    • 用途: 实现公平锁、任务队列等需要有序性的场景。
  3. 临时节点 (Ephemeral Node) - EPHEMERAL

    • 特点: 生命周期绑定到客户端会话。 会话结束(主动断开或超时)时,节点自动被删除不能有子节点。
    • 命令: create -e /path data
    • 用途: 服务注册与发现(服务在线则在特定路径下创建临时节点,下线则节点消失)、分布式锁持有者标识集群成员在线状态
  4. 临时顺序节点 (Ephemeral Sequential Node) - EPHEMERAL_SEQUENTIAL

    • 特点: 临时节点 + 创建时 ZK 自动在路径名后缀一个单调递增的序号会话结束自动删除。
    • 命令: create -e -s /path data
    • 用途: 实现分布式锁(如 Curator 的 InterProcessMutex)。多个客户端在锁节点下创建临时顺序节点,序号最小的获得锁。释放锁或会话结束节点删除后,下一个序号最小的获得锁。避免羊群效应。
  5. 容器节点 (Container Node) - CONTAINER (3.5.3+ 引入)

    • 特点: 特殊持久节点。当它的最后一个子节点被删除后,容器节点会在未来的某个时刻被服务器自动删除(异步任务清理)。不能有子节点? (可以创建子节点,但空时会被删)
    • 命令: create -c /path data
    • 用途: 适合存放动态子节点列表的场景(如分组、队列),当组内成员全部退出时自动清理组节点。
  6. 带过期时间的持久节点 (Persistent Node with TTL) - PERSISTENT_WITH_TTL (3.5.3+ 引入)

    • 特点: 持久节点,但如果在 TTL (Time-To-Live) 毫秒内没有被修改且没有子节点,则会被服务器自动删除。TTL 必须 > 0。
    • 命令: create -t <ttl> /path data (e.g., create -t 30000 /path data 表示 30 秒 TTL)
    • 用途: 需要在一定不活动时间后自动清理的持久化数据。
  7. 带过期时间和序号的持久节点 (Persistent Sequential Node with TTL) - PERSISTENT_SEQUENTIAL_WITH_TTL (3.5.3+ 引入)

    • 特点: 带过期时间的持久节点 + 创建时自动添加序号后缀。
    • 命令: create -s -t <ttl> /path data
    • 用途: 需要有序性且需要自动清理的持久化数据。

总结关键点:

  • 持久/临时: 区分节点生命周期是否依赖会话 (-e)。
  • 顺序: 节点名是否自动添加序号 (-s)。
  • 容器/TTL: 提供额外的自动清理机制 (-c, -t),需注意版本要求 (>=3.5.3)。

掌握 Zookeeper 的核心概念、选举机制(尤其是三要素比较和过半原则防脑裂)以及不同节点类型的适用场景,是理解和应用分布式系统的关键,也是面试中的高频考点。