Zookeeper实现分布式锁的原理

🧠 一、Zookeeper 分布式锁的核心原理

Zookeeper 的分布式锁基于其两个特性:

  1. 临时节点(Ephemeral Node)

    • 客户端与 ZK 会话断开后,节点自动删除。
    • 这样可以防止"锁被永久占用"。
  2. 有序节点(Sequential Node)

    • 每次创建节点时,ZK 会在名称后自动追加递增序号,如:

      csharp 复制代码
      /lock/lock-00000001
      /lock/lock-00000002
    • 这样天然可以实现「排队」。

🔩 二、Zookeeper 分布式锁算法原理

假设锁路径是 /lock

  1. 客户端尝试加锁

    • 每个客户端创建一个临时顺序节点:

      csharp 复制代码
      /lock/lock-00000001
  2. ZK 排序所有子节点

    • 客户端获取 /lock 下的所有子节点,判断自己是不是最小的。
    • 如果自己是最小节点 → 获取锁成功
    • 否则 → 监听前一个节点(例如自己是 00000005,就监听 00000004)。
  3. 锁释放(解锁)

    • 客户端完成任务后,删除自己的临时节点。
    • 监听该节点的下一个客户端会收到通知,从而重新竞争锁。

🔄 三、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 的底层逻辑其实和上文算法一致:

  1. 创建 /distribute-lock/lock-xxxx 临时顺序节点。
  2. 判断是否为最小节点。
  3. 如果不是,监听前一个节点。
  4. 前一个节点释放时收到 Watcher 事件,重新判断。
  5. 最终释放时删除节点。

Curator 内部封装了:

  • 异常重试机制
  • 会话断开自动释放
  • 可重入锁支持

🧾 六、Zookeeper 分布式锁的优缺点

优点 缺点
公平锁,严格按顺序 性能较 Redis 锁慢(网络开销 + ZK 延迟)
可重入锁、自动失效 依赖 ZK 集群的高可用性
强一致性 部署复杂度较高
相关推荐
王景程3 小时前
让IOT版说话
后端·python·flask
苏三的开发日记3 小时前
Redis实现分布式锁的原理
后端
阿豪啊4 小时前
Prisma ORM 入门指南:从零开始的全栈技能学习之旅
javascript·后端·node.js
optimistic_chen4 小时前
【Java EE进阶 --- SpringBoot】统一功能处理(拦截器)
spring boot·后端·java-ee·log4j·拦截器
苏三的开发日记4 小时前
什么是幂等,幂等如何实现
后端
逻极5 小时前
变量与可变性:Rust中的数据绑定
开发语言·后端·rust
一缕茶香思绪万堵5 小时前
028.爬虫专用浏览器-抓取#shadowRoot(closed)下
java·后端
panco681205 小时前
Ristretto - Golang高性能内存缓存管理库
后端
Cache技术分享6 小时前
226. Java 集合 - Set接口 —— 拒绝重复元素的集合
前端·后端