本文目录导读:

ZooKeeper 利用临时顺序节点可以实现一套高效且公平的分布式锁(通常称为 “排他锁” 或 “写锁” )。
核心思想是:每个客户端尝试加锁时,在锁路径下创建一个临时顺序节点,通过比较自己创建的节点序号是否为当前路径下最小的,来判断是否获得锁,如果没获得锁,则监听前一个节点的删除事件。
以下是基于临时顺序节点实现分布式锁的完整原理、流程和代码示例。
核心概念回顾
- 临时节点 (Ephemeral):客户端会话断开后,节点自动删除,有效防止死锁(客户端崩溃锁自动释放)。
- 顺序节点 (Sequential):ZooKeeper 自动在节点名后追加一个全局唯一且递增的序号(如
lock-0000000001)。 - Watch 机制:客户端可以监听节点的变化(如删除、数据变化)。
实现原理与流程
假设锁的根节点为 /locks。
-
创建锁节点:每个想获取锁的客户端,在
/locks下创建临时顺序节点。- 客户端 A 创建
/locks/lock-0000000001 - 客户端 B 创建
/locks/lock-0000000002 - 客户端 C 创建
/locks/lock-0000000003
- 客户端 A 创建
-
判断获取锁:客户端获取
/locks下的所有子节点列表,并按序号排序。- 如果自己创建的节点是列表中序号最小的节点,则成功获取锁。
- 如果自己不是最小的,说明锁已被其他客户端持有。
-
等待与监听:
- 如果未获取到锁,客户端不会盲目轮询,而是监听比自己节点序号小的最后一个节点(即前一个节点)。
- 客户端 B 监听
/locks/lock-0000000001;客户端 C 监听/locks/lock-0000000002。 - 这种机制形成了一个等待队列,保证了公平性(FIFO,先进先出)。
-
释放锁:
- 正常释放:客户端处理完业务逻辑后,主动删除自己创建的临时节点。
- 异常释放:客户端会话过期或宕机,ZooKeeper 自动删除该临时节点。
-
唤醒后续节点:
- 客户端 A 释放锁(删除
/locks/lock-0000000001)。 - 客户端 B 监听到 A 节点被删除的事件,重新执行步骤 2,检查自己是否为最小序号节点,此时它是最小的,因此成功获取锁。
- 客户端 A 释放锁(删除
代码示例 (Java + Apache Curator)
在实际开发中,强烈建议使用 Curator 框架,它已经将上述流程封装成优雅的 InterProcessMutex,并处理了各种边界情况(如羊群效应、惊群效应等)。
虽然问题要求实现原生逻辑,但出于实用性,这里提供 Curator 的使用方式:
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 ZkLockExample {
private static final String ZK_ADDRESS = "127.0.0.1:2181";
private static final String LOCK_PATH = "/locks/my_lock";
public static void main(String[] args) throws Exception {
// 1. 创建 Curator 客户端
CuratorFramework client = CuratorFrameworkFactory.newClient(
ZK_ADDRESS,
new ExponentialBackoffRetry(1000, 3)
);
client.start();
// 2. 创建分布式锁(本质就是临时顺序节点)
InterProcessMutex lock = new InterProcessMutex(client, LOCK_PATH);
try {
// 3. 获取锁(支持超时)
if (lock.acquire(10, TimeUnit.SECONDS)) {
System.out.println("获取锁成功,执行业务逻辑...");
// 模拟业务操作
Thread.sleep(5000);
} else {
System.out.println("获取锁失败,超时!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 4. 释放锁
if (lock.isAcquiredInThisProcess()) {
lock.release();
System.out.println("释放锁成功");
}
}
// 关闭客户端
client.close();
}
}
核心原理对比:临时顺序节点 vs 临时节点
| 特性 | 临时顺序节点 (推荐) | 临时节点 (不推荐) |
|---|---|---|
| 公平性 | 公平(FIFO),按请求顺序排队 | 不公平,抢锁顺序不确定 |
| 羊群效应 | 低,只监听前一个节点(链式监听) | 高,所有等待者监听同一个锁节点,释放时惊群 |
| 性能 | 高(O(n) 仅监听一个节点) | 低(O(N^2) 所有节点同时被唤醒) |
| 实现复杂度 | 稍复杂(需要排序和链式监听) | 简单,但性能差 |
| 死锁处理 | 自动删除(临时 + 会话超时) | 自动删除(临时 + 会话超时) |
优缺点分析
优点:
- 公平锁:严格按照请求顺序获取锁,避免了线程“饥饿”问题。
- 避免羊群效应:每个客户端只监听前一个节点,锁释放时只有一个客户端被唤醒,而不是所有客户端都去抢锁。
- 没有死锁:利用临时节点特性,客户端崩溃会自动释放锁,服务端无需额外处理。
- 高可用:ZooKeeper 集群保证锁服务的高可用。
缺点:
- 性能瓶颈:每次锁操作都需要与 ZooKeeper 集群进行多次网络通信(创建节点、获取子节点列表、设置监听),在高并发场景下,ZooKeeper 可能成为瓶颈(吞吐量通常低于 Redis)。
- 需考虑会话超时:如果业务执行时间过长,超过 ZooKeeper 会话超时时间,锁会被自动释放,导致其他客户端获取到锁(可能产生并发问题),需要权衡业务耗时和会话超时时间。
- 实现复杂度:相比 Redis 的 Redlock 或简单的 SETNX,手动实现原生 API 稍复杂(Curator 已解决此问题)。
应用场景
- 需要严格公平锁的场景:如任务调度、全局 ID 生成、资源顺序访问。
- 对锁可靠性要求极高的场景:如金融交易、库存扣减、关键配置更新。
- 对高吞吐量要求不极端,但对一致性要求高的场景。
使用 ZooKeeper 临时顺序节点实现分布式锁:
- 核心机制:创建临时顺序节点 + 监听前一个节点。
- 最大优势:公平、避免羊群效应、自动释放(安全)。
- 工业推荐:直接使用 Apache Curator 的
InterProcessMutex,生产验证、简单可靠。 - 注意事项:合理设置会话超时时间,避免业务未完成锁被提前释放。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播