🧠 一、Zookeeper 分布式锁的核心原理
Zookeeper 的分布式锁基于其两个特性:
-
临时节点(Ephemeral Node) :
- 客户端与 ZK 会话断开后,节点自动删除。
- 这样可以防止"锁被永久占用"。
-
有序节点(Sequential Node) :
-
每次创建节点时,ZK 会在名称后自动追加递增序号,如:
csharp/lock/lock-00000001 /lock/lock-00000002 -
这样天然可以实现「排队」。
-
🔩 二、Zookeeper 分布式锁算法原理
假设锁路径是 /lock:
-
客户端尝试加锁
-
每个客户端创建一个临时顺序节点:
csharp/lock/lock-00000001
-
-
ZK 排序所有子节点
- 客户端获取
/lock下的所有子节点,判断自己是不是最小的。 - 如果自己是最小节点 → 获取锁成功。
- 否则 → 监听前一个节点(例如自己是 00000005,就监听 00000004)。
- 客户端获取
-
锁释放(解锁)
- 客户端完成任务后,删除自己的临时节点。
- 监听该节点的下一个客户端会收到通知,从而重新竞争锁。
🔄 三、Zookeeper 分布式锁的完整流程图(文字版)
arduino
Client A 创建 /lock/lock-00000001 → 成功获得锁
Client B 创建 /lock/lock-00000002 → 监听 00000001
Client C 创建 /lock/lock-00000003 → 监听 00000002
当 Client A 删除节点 → Client B 收到通知 → 获得锁
当 Client B 删除节点 → Client C 收到通知 → 获得锁
💡 四、Java 中的常见实现方式
✅ 方式 1:使用 Curator 框架(最常用)
Curator 是 Apache 官方维护的 Zookeeper 高级客户端 ,封装了分布式锁、选主、Barrier 等高级特性。
推荐直接用它来实现锁,而不是自己操作 ZNode。
🧱 Maven 依赖坐标
xml
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.6.0</version>
</dependency>
(版本号可查最新的 Apache Curator Releases)
🧩 代码示例
java
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import java.util.concurrent.TimeUnit;
public class ZookeeperLockDemo {
private static final String ZK_ADDRESS = "127.0.0.1:2181";
private static final String LOCK_PATH = "/distribute-lock";
public static void main(String[] args) {
// 1️⃣ 创建客户端
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(ZK_ADDRESS)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
// 2️⃣ 创建分布式锁对象
InterProcessMutex lock = new InterProcessMutex(client, LOCK_PATH);
try {
// 3️⃣ 尝试获取锁,等待最多5秒
if (lock.acquire(5, TimeUnit.SECONDS)) {
try {
System.out.println(Thread.currentThread().getName() + " 获得锁,执行任务...");
Thread.sleep(3000);
} finally {
lock.release();
System.out.println(Thread.currentThread().getName() + " 释放锁");
}
} else {
System.out.println("获取锁超时");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
client.close();
}
}
}
⚙️ 五、Curator 实现锁的底层原理(简述)
InterProcessMutex 的底层逻辑其实和上文算法一致:
- 创建
/distribute-lock/lock-xxxx临时顺序节点。 - 判断是否为最小节点。
- 如果不是,监听前一个节点。
- 前一个节点释放时收到 Watcher 事件,重新判断。
- 最终释放时删除节点。
Curator 内部封装了:
- 异常重试机制
- 会话断开自动释放
- 可重入锁支持
🧾 六、Zookeeper 分布式锁的优缺点
| 优点 | 缺点 |
|---|---|
| 公平锁,严格按顺序 | 性能较 Redis 锁慢(网络开销 + ZK 延迟) |
| 可重入锁、自动失效 | 依赖 ZK 集群的高可用性 |
| 强一致性 | 部署复杂度较高 |