Apache ZooKeeper
ZooKeeper 的分布式协调主笔记:从 znode、session、ephemeral/sequential node、watch、quorum 到服务发现、选主、分布式锁与和 etcd/Kafka 的边界。
[!info] related notes
- 所属 MOC:Backend 常用中间件与基础设施 MOC、Distributed Systems MOC
- 前置:Leader Election、分布式锁、服务发现
- 对比:etcd
Apache ZooKeeper
[!abstract] 一句话定位 ZooKeeper 是分布式协调服务,以层级 ZNode、Session、Ephemeral / Sequential Node、Watch 和 Quorum 为核心,常用于成员关系、配置、选主和锁。它存的是小规模协调状态,不是通用业务数据库或消息日志。
核心命名空间
/
├─ services/api/instance-0001
├─ config/payment
└─ election/worker-0000000003
ZNode 包含少量 Data、Version、Children 和 ACL。数据读取和写入以单个 ZNode 为原子边界。
Persistent、Ephemeral 与 Sequential
Persistent ZNode
除非显式删除,否则长期存在,适合稳定配置和命名空间结构。
Ephemeral ZNode
与创建它的 Session 生命周期绑定:
Session alive → Ephemeral ZNode exists
Session expires → ZNode removed
适合服务注册、成员关系和临时 Ownership。Ephemeral Node 不能拥有 Children。
Sequential ZNode
创建时自动附加递增序号:
/election/node-0000000001
/election/node-0000000002
适合 Leader Election、Lock Queue 和有序 Ownership。
Session 不是 TCP Connection
客户端可以在 Session Timeout 内重连其他 ZooKeeper Server 并继续同一 Session。真正 Session Expiration 后:
- Ephemeral ZNode 被删除;
- Watch / Ownership 语义改变;
- 旧 Client 必须停止把自己当有效 Owner。
网络短暂断开时,Client 不能确定自己的状态是否仍然最新,应进入保守模式。
Watch
经典 Watch 是一次性触发:
read + register watch
→ znode changes
→ receive event
→ watch consumed
→ re-read and register again
ZooKeeper 3.6+ 也支持 Persistent / Recursive Watch,但 Watch 仍更适合“状态变了,请重新读取并 Reconcile”,不是持久消息日志。
[!warning] Watch 不能保证看到每个中间状态 断线、重注册窗口和连续多次变化都可能让 Client 只观察到最终状态。业务正确性应建立在读取当前状态和 Version 上,而不是把每个 Watch Event 当不可丢事件。
一致性保证
ZooKeeper 的写操作可线性化排序;普通读可以由连接的 Server 本地响应,因此可能读到相对最新 Quorum 状态较旧的数据。
核心保证包括:
- 客户端操作顺序得到保持;
- 更新原子成功或失败;
- 同一 Session 切换 Server 不会倒退到更旧视图;
- 成功确认的更新不会在故障恢复后被回滚。
Write → globally ordered
Read → fast, but may be stale
因此不能简单说“ZooKeeper 所有读写都是强一致”。
Ensemble、Leader 与 Quorum
ZooKeeper 集群由 Ensemble 组成。写入经 Leader 排序并由多数派确认:
Client Write
→ Leader proposal
→ Followers acknowledge
→ quorum commit
→ apply
常见使用 3 或 5 个 Voting Members。增加成员会提高可容忍故障数,也增加写协调成本。
三个典型 Recipe
服务发现
service instance
→ create ephemeral /services/api/<id>
client
→ list children + watch
→ maintain endpoint set
Leader Election
candidates create ephemeral sequential nodes
→ smallest sequence becomes leader
→ each follower watches predecessor
只 Watch 前驱可减少所有候选者同时被唤醒的 Herd Effect。
Distributed Lock
同样可用 Ephemeral Sequential Node 形成等待队列。但保护外部数据库、文件或设备时,仍需要 Version / Fencing Token,防止 Session 已失效的旧 Owner 继续执行。
ZooKeeper 与 etcd
ZooKeeper
→ hierarchical namespace
→ Session / Ephemeral / Sequential ZNode
→ Watch recipes
etcd
→ flat ordered KV ranges
→ global Revision
→ Lease / Watch / Txn / CAS
→ Raft API model
现代云原生系统常使用 etcd;传统 Java、大数据和旧基础设施生态中仍广泛存在 ZooKeeper。
Kafka 的现代版本边界
Kafka 过去长期依赖 ZooKeeper 管理元数据。Kafka 4.x 已移除 ZooKeeper 模式,只支持 KRaft。
因此:
- 学 ZooKeeper 仍有分布式协调价值;
- 学现代 Kafka 时不要再把 ZooKeeper 当默认组件;
- 维护 Kafka 3.x 旧集群时才需要理解迁移历史。
适用与不适用
适合:配置协调、成员关系、服务发现、Leader Election、Distributed Lock Recipe、小规模关键元数据。
不适合:大量业务数据、高吞吐 Event Log、Cache、大文件、复杂查询和分析。
高频故障
Session Expiration
Client Pause、网络问题或处理 Watch 太慢都可能导致 Session 失效。恢复时必须重建 Ephemeral State,而不是继续使用旧 Ownership。
Herd Effect
大量 Client Watch 同一 ZNode,变化时同时唤醒。使用分层状态、前驱 Watch、缓存和 Reconciliation 降低冲击。
大 ZNode / 大 Children Set
ZooKeeper 为协调而设计。大 Value、巨量 Children 或高频写会增加内存、网络和 Watch 成本。
磁盘与 Swap
事务日志磁盘延迟和 Swap 会直接影响写入及时性与 Session 稳定,应避免与高 I/O 工作负载争用。
观测重点
leader / follower state
outstanding requests
watch count
latency min/avg/max
znode count / data size
session count / expiry
packet rate
transaction log / snapshot / disk latency
quorum health
面试掌握标准
- ZNode、Persistent、Ephemeral、Sequential;
- Session 与 TCP Connection 的区别;
- Watch 的一次性/持久模式和漏中间状态边界;
- 写线性化、读可能 stale;
- Quorum 与 Leader;
- Election / Lock Recipe;
- ZooKeeper 与 etcd 的差异;
- Kafka 4.x 不再依赖 ZooKeeper。
官方资料
- Overview and guarantees: https://zookeeper.apache.org/doc/current/zookeeperOver.html
- Programmer’s guide: https://zookeeper.apache.org/doc/current/zookeeperProgrammers.html
- Internals and consistency: https://zookeeper.apache.org/doc/current/zookeeperInternals.html
- Administration: https://zookeeper.apache.org/doc/current/zookeeperAdmin.html