当前位置: 首页 > ops >正文

分布式微服务--ZooKeeper作为分布式锁

看这篇博客之前可以先去了解博主的另一篇讲解ZooKeeper的博客:分布式微服务--ZooKeeper的客户端常用命令 & Java API 操作-CSDN博客

1. 为什么需要分布式锁?

在分布式系统中,多个服务节点可能同时访问或修改同一份共享资源(例如数据库记录、缓存数据、文件等)。
如果不加限制,容易出现数据不一致或竞争条件。
因此,需要一种机制来保证:同一时刻只有一个节点能访问某资源。这就是分布式锁的意义。

常见分布式锁实现方式:

  • 基于 Redis

  • 基于 ZooKeeper

  • 基于 数据库

其中 ZooKeeper 实现分布式锁更安全可靠,因为它的核心是 强一致性 + 临时顺序节点机制


2. ZooKeeper 的分布式锁原理

ZooKeeper 提供的 临时节点(Ephemeral)有序节点(Sequential) 是实现分布式锁的关键。

核心思想:

  1. 创建锁节点
    所有客户端到某个固定路径(如 /lock)下创建一个 临时顺序节点,例如:

    /lock/lock_00000001
    /lock/lock_00000002
    /lock/lock_00000003
    
    • 临时节点保证客户端宕机或断开时自动删除。

    • 顺序节点保证多个客户端的排队顺序。

  2. 获取锁
    客户端判断自己创建的节点是否是 序号最小的节点

    • 如果是 → 获取锁成功。

    • 如果不是 → 监听比自己小的前一个节点,等待其释放。

  3. 释放锁
    客户端执行完业务逻辑后,删除自己的节点 → 唤醒下一个等待者。


3. 分布式锁实现步骤

假设我们要对某个共享资源 order 加锁。

1)获取锁

  • 客户端在 /lock/order 下创建临时顺序节点,例如:
    /lock/order/lock_00000010

  • 获取 /lock/order 下所有子节点,并排序。

  • 如果自己是最小的节点 → 获得锁。

  • 否则 → 监听自己前一个节点。

2)释放锁

  • 客户端完成业务逻辑后,删除自己的节点。

  • ZooKeeper 会通知下一个等待者。

3)异常情况

  • 如果客户端宕机或与 ZooKeeper 断开连接,ZooKeeper 会自动删除临时节点,从而避免锁“死锁”。


4. ZooKeeper 分布式锁代码示例

基于 Curator(推荐)

Curator 是 ZooKeeper 的一个高级客户端,封装了分布式锁。


样例代码(本次使用的锁是InterProcessMutex):

xml

  <!--curator--><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.0.0</version></dependency>

代码


/*** 模拟12306售票系统 —— 使用 Zookeeper 分布式锁保证并发安全*/
public class Ticket12306 implements Runnable{// 模拟数据库中的票数private int tickets = 10;// 分布式可重入锁对象(Curator 提供)private InterProcessMutex lock ;public Ticket12306(){// 1. 定义重试策略:初始等待时间 3 秒,最多重试 10 次RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);// 2. 通过 builder 模式创建 CuratorFramework 客户端CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181") // Zookeeper 连接地址.sessionTimeoutMs(60 * 1000) // 会话超时时间.connectionTimeoutMs(15 * 1000) // 连接超时时间.retryPolicy(retryPolicy) // 指定重试策略.build();// 3. 开启连接client.start();// 4. 创建分布式锁对象,指定锁的路径(ZK 节点)//   不同客户端只要路径一样,就能实现互斥lock = new InterProcessMutex(client,"/lock");}@Overridepublic void run() {while(true){try {// 1. 尝试获取锁,最多等待 3 秒lock.acquire(3, TimeUnit.SECONDS);// 2. 拿到锁后执行业务逻辑 —— 卖票if(tickets > 0){System.out.println(Thread.currentThread()+":" + tickets);Thread.sleep(100); // 模拟业务处理耗时tickets--; // 卖出一张票}} catch (Exception e) {e.printStackTrace();} finally {// 3. 无论如何,最后都要释放锁,避免死锁try {lock.release();} catch (Exception e) {e.printStackTrace();}}}}
}

测试:

/*** 测试类:模拟多个客户端同时抢票*/
public class LockTest {public static void main(String[] args) {// 创建一个 Ticket12306 对象(共享 10 张票)Ticket12306 ticket12306 = new Ticket12306();// 创建两个线程,模拟两个不同的售票平台(携程、飞猪)Thread t1 = new Thread(ticket12306,"携程");Thread t2 = new Thread(ticket12306,"飞猪");// 启动线程,同时卖票t1.start();t2.start();}
}

总结:

  1. InterProcessMutex 是 Curator 提供的 可重入分布式锁,底层用 Zookeeper 的临时顺序节点实现。

  2. lock.acquire() 获取锁,获取不到会阻塞(或超时返回 false)。

  3. lock.release() 释放锁,必须写在 finally,避免异常导致死锁。

  4. 这种方式可以模拟 多进程/多机器的并发安全,保证同一时刻只有一个客户端在修改票数。


5. ZooKeeper 分布式锁的优缺点

✅ 优点

  • 强一致性:ZK 的数据一致性保证了锁的可靠性。

  • 自动释放:客户端宕机,临时节点会自动删除,避免死锁。

  • 公平性:顺序节点机制,保证先来先服务。

❌ 缺点

  • 性能较低:每次加锁/解锁都需要与 ZK 通信,适合低并发场景。

  • 部署复杂:需要搭建 ZooKeeper 集群。

  • 羊群效应:节点删除时可能触发大量客户端通知(不过监听前一个节点可以缓解)。


6. 使用场景

  • 订单系统(防止超卖)

  • 分布式定时任务(同一时间只允许一个节点执行)

  • 共享资源访问控制(文件、缓存等)


7. 总结

  • ZooKeeper 分布式锁依赖 临时顺序节点 + watcher 机制

  • Curator 封装了常用的锁(InterProcessMutex),开发更方便。

  • 适合一致性要求高、并发量不是极高的业务场景。

  • 高并发下更推荐 Redis 分布式锁(性能更好,但需要妥善解决可靠性问题)。

http://www.xdnf.cn/news/19957.html

相关文章:

  • Spring如何解决循环依赖:深入理解三级缓存机制
  • Android13 系统源码核心目录解析
  • css margin外边距重叠/塌陷问题
  • AI时代企业获取精准流量与实现增长的GEO新引擎
  • Android14实现Settings左右分屏显示的 代码修改
  • 智能相机还是视觉系统?一文讲透工业视觉两大选择的取舍之道
  • MCP驱动企业微信智能中枢:企业级机器人服务构建全攻略
  • 嘎嘎厉害!耐达讯自动化RS485转Profinet网关就是食品温控的“天选之子”
  • vscode连接SSH
  • 25高教社杯数模国赛【C题超高质量思路+可运行代码】第十弹
  • PostgreSQL15——DML 语句
  • jodconverter将word转pdf底层libreoffice的问题
  • 企业微信AI怎么用才高效?3大功能+5个实操场景,实测效率提升50%
  • Linux服务器暴走,用Netdata+cpolar轻松驯化
  • 数据库查询优化
  • 高级RAG策略学习(六)——Contextual Chunk Headers(CCH)技术
  • MySQL InnoDB 的 MVCC 机制
  • 在选择iOS代签服务前,你必须了解的三大安全风险
  • Opencv C++ 教程-人脸识别
  • AI驱动健康升级:新零售企业从“卖产品”到“卖健康”的转型路径
  • 人形机器人控制系统核心芯片从SoC到ASIC的进化路径
  • 机器学习与Backtrader的融合构建自适应交易策略
  • 动态规划入门:从记忆化搜索到动态规划
  • 从0开始学习Java+AI知识点总结-30.前端web开发(JS+Vue+Ajax)
  • JavaSe之多线程
  • 残差网络的介绍
  • 【代码随想录算法训练营——Day2】数组——209.长度最小的子数组、59.螺旋矩阵II、区间和、开发商购买土地
  • “人工智能+”的新范式:应用赋能与风险应对
  • 不会战略、不会融资、不会搭团队?别叫自己 CTO
  • /Users/yourname/Library/Developer/Xcode 文件夹里面各子文件夹作用