Zookeeper 是干什么的?
结论先行:ZooKeeper(ZK)是一个高可用的分布式协调服务,用“类文件系统的树形命名空间 + 节点监听 + 会话”这三样原语,让分布式系统能就“谁活着、谁当主、配置是什么、锁在谁手里”达成一致。它不存业务数据,只存协调元数据。
一、它提供的四样原语
| 原语 | 含义 | 用途 |
|---|---|---|
| 节点(znode) | 路径唯一的树形数据节点,可带版本 | 命名、配置、元数据 |
| 监听(watch) | 对节点变更的一次性订阅 | 通知、服务发现 |
| 会话(session) | 客户端与集群的临时身份,带超时 | 存活探测、临时节点自动清理 |
| 顺序号(sequential) | 父节点下自增序号 | 队列、分布式锁排队 |
二、集群角色与写路径
┌──────────┐
写请求 → │ Leader │ ── 提案 → 事务日志 → 广播给 Follower
└────┬─────┘
┌────────┴────────┐
┌────▼────┐ ┌────▼────┐ ┌──────────┐
│Follower │ │Follower │ │ Observer │
└────┬────┘ └────┬────┘ └────┬─────┘
└──────── 过半 ACK 才算成功 ────────┘
读请求可就地返回
Leader 统一处理写,Follower 参与投票与读,Observer 只读不投票,用来扩展读能力而不拖慢写性能。
三、最小可用示例
# 启动单机(开发环境)
bin/zkServer.sh start
# 常用命令
create /app "root" # 持久节点
create -e /app/lock "holder" # 临时节点,会话结束即删
create -s /app/task "job-" # 顺序节点,自动补 10 位序号
get /app
set /app "v2"
ls /app
delete /app/lock
Java 侧一般用 Apache Curator 封装,避免自己处理重连与重注册:
CuratorFramework client = CuratorFrameworkFactory.newClient(
"127.0.0.1:2181", new ExponentialBackoffRetry(1000, 3));
client.start();
client.create().withMode(CreateMode.EPHEMERAL).forPath("/app/inst-1", "10.0.0.1".getBytes());
四、它不擅长什么
| 场景 | 原因 |
|---|---|
| 存大对象 | 单节点默认上限 1MB,超过影响同步性能 |
| 高频写 | 每次写都要过半确认 + 落盘事务日志,QPS 有限 |
| 当数据库用 | 无查询、无索引、无事务回滚,只适合元数据 |
| 大规模临时节点 | 数十万临时节点时,会话超时清理会造成尖峰 |
五、与其他协调组件的定位
| 组件 | 一致性协议 | 数据模型 | 典型使用者 |
|---|---|---|---|
| ZooKeeper | ZAB | 树形 znode | Dubbo、HBase、Kafka 旧版 |
| etcd | Raft | 扁平 KV | Kubernetes |
| Consul | Raft | KV + 服务目录 | 多语言微服务 |
三者都能做“注册 + 配置 + 选主”,差别在生态与数据模型,不在能力有无。
常见追问
- 追问:ZK 和 etcd 有什么区别?etcd 用 Raft,接口是 KV + watch,云原生生态更好;ZK 用 ZAB,树形模型与顺序节点对“排队锁”更自然。
- 追问:ZK 为什么要求奇数节点?quorum 需要过半,偶数节点并不增加容错能力,只增加成本。
- 追问:ZK 挂了业务会立刻不可用吗?不会立刻,客户端有本地缓存与重连;但无法注册新实例、无法感知下线,属于“降级可用”。
小结
- ZK 是协调服务,四样原语是 znode、watch、session、顺序号。
- 写走 Leader + 过半确认,读可就地返回,Observer 扩展读。
- 单节点 1MB、写 QPS 有限,别把它当存储用。
- 生产上用 Curator,别手写重连与重注册逻辑。