ZooKeeper 分布式协调:选举、锁与配置
ZooKeeper 分布式协调:选举、锁与配置
ZooKeeper 是 Apache 顶级项目,专为分布式系统提供高性能、强一致性的协调服务。它好比分布式系统的“交通警察”,帮你解决选举、分布式锁、配置同步等棘手问题,让你专注于业务逻辑。本教程从零开始,带你快速掌握这三大核心场景。
1. 为什么需要 ZooKeeper?
在微服务集群、大数据组件(Hadoop、Kafka)中,各节点需要就某件事达成一致:谁当 Master?哪个节点获取锁?配置变更如何通知全网?若自研这些协调逻辑,你将坠入网络超时、脑裂、顺序性等深渊。ZooKeeper 提供类似文件系统的树形数据模型(Znode)和强大的监听机制(Watcher),以**原子广播协议(ZAB)**保证数据一致性,让你开箱即用实现协调需求。
2. 环境准备
- JDK 8+
- 下载 ZooKeeper:https://zookeeper.apache.org/releases.html
- 解压后,
conf/zoo.cfg最小配置:
tickTime=2000
dataDir=/tmp/zookeeper
clientPort=2181
- 启动:
bin/zkServer.sh start(Windows 用bin/zkServer.cmd) - 连接测试:
bin/zkCli.sh -server 127.0.0.1:2181
3. 核心概念速览
| 概念 | 说明 |
|---|---|
| Znode | 数据节点,类似文件路径。分为持久(PERSISTENT)、临时(EPHEMERAL)、持久顺序(PERSISTENT_SEQUENTIAL)、临时顺序(EPHEMERAL_SEQUENTIAL)。 |
| Watcher | 客户端可在 Znode 上注册监听,当节点数据变化或子节点变化时,服务端会推送通知。一次性触发。 |
| Session | 客户端连接会话。心跳维持,超时后临时节点自动删除。 |
| ZAB 协议 | ZooKeeper Atomic Broadcast,保证集群数据更新的顺序一致性和原子性。 |
4. 分布式协调三大实战
4.1 Leader 选举
场景
集群需要一个 Master 执行调度,当 Master 宕机,备用节点能自动接管。
原理
利用临时顺序节点和最小序号原则:
- 所有候选节点在同一路径(如
/election)下创建临时顺序节点,命名类似node-0000000001。 - 节点获取
/election下所有子节点,判断自己是否是序号最小的节点。 - 是 → 成为 Leader。
- 否 → 对序号比自己小且最靠近自己的节点注册 Watcher,进入等待。
- Leader 宕机 → 临时节点消失 → 等待的节点收到通知,重新选举。
代码演示(Java + Curator)
Curator 是 ZooKeeper 的高级客户端,封装了选举等场景。
依赖(Maven):
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.5.0</version>
</dependency>
Leader 选举示例:
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
LeaderSelectorListener listener = new LeaderSelectorListener() {
@Override
public void takeLeadership(CuratorFramework client) throws Exception {
System.out.println("I am the leader now.");
// 模拟领导工作
Thread.sleep(10000);
// 退出领导权后,自动重新选举
}
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState) {
// 处理连接状态变化
}
};
LeaderSelector selector = new LeaderSelector(client, "/leader", listener);
selector.autoRequeue(); // 失去领导权后自动加入选举
selector.start();
// 保持程序运行
System.in.read();
selector.close();
client.close();
运行多个实例,你会看到只有一个实例打印 “I am the leader now.”,宕掉后另一个会立即接管。
4.2 分布式锁
场景
多服务互斥访问共享资源(如扣减库存),避免并发冲突。
原理
利用临时顺序节点 + 等待/通知实现公平可重入锁。
- 在锁路径(如
/lock)下创建临时顺序节点。 - 获取所有子节点,判断自己是否序号最小:是 → 获取锁。
- 否 → 对序号比自己小且紧邻的节点注册 Watcher,阻塞等待。
- 释放锁时删除节点,下一个节点被唤醒。
代码演示(Curator 封装)
InterProcessMutex lock = new InterProcessMutex(client, "/lock");
if (lock.acquire(3, TimeUnit.SECONDS)) {
try {
// 执行业务逻辑
System.out.println("Doing critical work...");
} finally {
lock.release();
}
}
InterProcessMutex自动处理临时节点创建、监听和续约问题。生产环境必须使用。
4.3 配置管理
场景
动态推送配置(数据库连接串、开关阈值),无需重启服务。
原理
在 ZooKeeper 中存储配置数据,客户端监听对应 Znode 的变化。
- 初始化:将配置写入持久节点
/config/app1(值如 JSON)。 - 各个服务启动时读取该节点数据,并注册 Watcher。
- 配置更新:更新节点数据,所有监听客户端收到通知,重新拉取配置。
代码示例
// 读取并监听配置
String path = "/config/db";
byte[] data = client.getData().forPath(path);
System.out.println("初始配置: " + new String(data));
// 注册监听
CuratorCache cache = CuratorCache.build(client, path);
CuratorCacheListener listener = CuratorCacheListener.builder()
.forChanges((oldNode, newNode) -> {
if (newNode != null) {
System.out.println("配置变更: " + new String(newNode.getData()));
// 执行刷新逻辑
}
})
.build();
cache.listenable().addListener(listener);
cache.start();
// 修改配置(命令行):
// set /config/db '{"host":"new.db.com","port":3306}'
注意:Watcher 是一次性的,但 Curator Cache 会自动重复注册,实现持续监听。实现时记得对配置解析做容错处理。
5. 高级注意事项
5.1 临时节点与连接保活
临时节点的生命周期与 Session 绑定。网络抖动可能导致 Session 超时并删除临时节点,引发错误的锁释放或选举。请合理设置 sessionTimeout,并实现重试机制。
5.2 脑裂与 ZAB 协议
ZooKeeper 集群通过多数派(大于半数节点)存活保证一致性。部署奇数个节点(如3、5)以避免脑裂。Leader 选举和数据写入都遵循 ZAB 的两阶段提交。
5.3 性能边界
- ZooKeeper 适用于元数据协调,不宜作为大数据量存储。每个 Znode 数据应小于 1MB。
- Watcher 数量过多会增加服务端压力,避免注册根节点的全量子节点监听。
5.4 Curator 框架优势
原生 API 过于底层。Curator 提供了:
- 自动重连和重试策略
- 锁续约(自旋获取)
- 主子节点变更的连续监听(PathChildrenCache、NodeCache、TreeCache)
强烈建议所有 Java 项目使用 Curator。
6. 总结与下一步
通过 ZooKeeper,你可以用极简的节点模型和监听机制,将复杂的分布式协调问题变成标准化的几行代码。本教程覆盖的选举、锁、配置管理是分布式系统最常用的三种模式。
在你的实际项目中,可以继续学习:
- 分布式队列、屏障(Barrier) 等模式
- ZooKeeper 集群运维与监控(四字命令、Prometheus 集成)
- Apache Kafka、Hadoop HDFS 对 ZooKeeper 的深度使用案例
协调好分布式组件,你的系统架构将更健壮、更易扩展。开始动手构建你的第一个高可用 ZooKeeper 服务吧!