zookeeper Curator(4):分布式锁

文章目录

  • 分布式锁
  • 分布式锁的实现
  • zookeeper 分布式锁原理
  • Curator 实现分布式锁API
      • 1. InterProcessMutex(分布式可重入互斥锁)
      • 2. InterProcessSemaphoreMutex(分布式非可重入互斥锁)
      • 3. InterProcessReadWriteLock(分布式读写锁)
      • 4. InterProcessSemaphoreV2(分布式信号量)
      • 5. MultiSharedLock(多共享锁)
      • 方案对比与推荐
  • 模拟12306售票案例
      • 案例背景
      • 实现步骤
        • 1. 依赖引入
        • 2. ZooKeeper 连接配置
        • 3. 票务服务(核心逻辑)
        • 4. 模拟并发抢票
      • 关键点解析
      • 运行结果示例
      • 扩展优化

分布式锁

  • 在进行单机应用开发时,涉及女并发同步的时候,我们往往采用synchronized或者Lock的方式来解决多线程间的代码同步问题,这时多线程的运行都在同一个JVM之下,没有任何问题。
  • 但当我们的应用是分布式集群工作的情况下,属于多个JVM下的工作环境,跨JVM之间已经无法通过多线程的锁解决同步问题。
  • 那么就需要一种更高级的锁机制来处理这种跨机器的进程之间的数据同步问题,这就是分布式锁。

分布式锁的实现

  • 基于缓存实现分布式锁:redis,memcache
  • zookeeper实现分布式锁:Curator
  • 数据库层面实现分布式锁: 乐观锁,悲观锁

zookeeper 分布式锁原理

在这里插入图片描述

  • 核心思想: 当客户端要获取锁,则创建节点,使用完锁,则删除该节点。
  1. 客户端时,在lock节点下创建 临时顺序 节点。
  2. 然后获取lock下面的所有子节点,客户端获取到所有的子节点后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。使用完锁后,将该节点删除。
    3 如果发现自己创建的节点并非lock所有节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,同时对其注册事件监听,监听删除事件。
    4 如果发现比自己小的那个节点被删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是lock子节点中最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。

Curator 实现分布式锁API

Curator 提供了五种分布式锁方案,每种方案适用于不同的业务场景,以下是具体介绍:

1. InterProcessMutex(分布式可重入互斥锁)

  • 特点
    • 可重入:同一线程可多次获取锁,避免死锁。
    • 互斥性:基于 ZooKeeper 临时顺序节点实现全局互斥,确保任何时刻只有一个客户端持有锁。
    • 自动释放:通过临时节点的特性,客户端宕机时锁自动释放。
  • 适用场景
    • 需要线程安全的分布式资源访问(如订单处理、库存扣减)。
  • 示例代码
    CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", new ExponentialBackoffRetry(1000, 3));
    client.start();
    InterProcessMutex lock = new InterProcessMutex(client, "/locks/myLock");
    lock.acquire(); // 获取锁
    try {// 执行业务逻辑
    } finally {lock.release(); // 释放锁
    }
    

2. InterProcessSemaphoreMutex(分布式非可重入互斥锁)

  • 特点
    • 非可重入:同一线程重复获取锁会阻塞,避免误用导致的死锁。
    • 轻量级:相比可重入锁,实现更简单,性能略高。
  • 适用场景
    • 需要严格互斥且无需重入的场景(如分布式任务调度)。
  • 示例代码
    InterProcessSemaphoreMutex lock = new InterProcessSemaphoreMutex(client, "/locks/nonReentrantLock");
    lock.acquire(); // 获取锁
    try {// 执行业务逻辑
    } finally {lock.release(); // 释放锁
    }
    

3. InterProcessReadWriteLock(分布式读写锁)

  • 特点
    • 读写分离:允许多个读锁并发,写锁独占。
    • 公平性:基于 ZooKeeper 节点顺序实现公平锁,避免写锁饥饿。
  • 适用场景
    • 读多写少的场景(如分布式缓存更新、配置中心)。
  • 示例代码
    InterProcessReadWriteLock rwLock = new InterProcessReadWriteLock(client, "/locks/rwLock");
    InterProcessMutex readLock = rwLock.readLock(); // 读锁
    InterProcessMutex writeLock = rwLock.writeLock(); // 写锁
    readLock.acquire(); // 获取读锁
    try {// 执行读操作
    } finally {readLock.release(); // 释放读锁
    }
    

4. InterProcessSemaphoreV2(分布式信号量)

  • 特点
    • 资源限制:控制同时访问资源的客户端数量(如许可证数量)。
    • 动态调整:可通过 ZooKeeper 节点动态修改信号量值。
  • 适用场景
    • 限流控制(如 API 调用限流、连接池管理)。
  • 示例代码
    InterProcessSemaphoreV2 semaphore = new InterProcessSemaphoreV2(client, "/locks/semaphore", 3); // 允许3个客户端同时访问
    Lease lease = semaphore.acquire(); // 获取信号量
    try {// 执行业务逻辑
    } finally {semaphore.returnLease(lease); // 释放信号量
    }
    

5. MultiSharedLock(多共享锁)

  • 特点
    • 组合锁:允许客户端同时获取多个锁,实现跨资源的原子操作。
    • 避免死锁:通过全局顺序获取锁,防止死锁。
  • 适用场景
    • 需要跨多个资源协调的场景(如分布式事务)。
  • 示例代码
    List<String> lockPaths = Arrays.asList("/locks/resource1", "/locks/resource2");
    MultiSharedLock multiLock = new MultiSharedLock(client, lockPaths);
    multiLock.acquire(); // 获取所有锁
    try {// 执行跨资源操作
    } finally {multiLock.release(); // 释放所有锁
    }
    

方案对比与推荐

锁类型可重入并发性适用场景
InterProcessMutex需要线程安全的资源访问
InterProcessSemaphoreMutex严格互斥场景
InterProcessReadWriteLock读是读高/写低读多写少场景
InterProcessSemaphoreV2高(有限制)限流控制
MultiSharedLock跨资源原子操作
  • 推荐选择
    • 默认场景:优先使用 InterProcessMutex,兼顾安全性和灵活性。
    • 高性能读场景:选择 InterProcessReadWriteLock 的读锁。
    • 限流场景:使用 InterProcessSemaphoreV2 控制并发量。
    • 复杂协调场景MultiSharedLock 实现跨资源同步。

模拟12306售票案例

以下是使用 Curator 的 InterProcessMutex 模拟 12306 售票系统 的分布式锁案例,解决高并发下超卖问题:


案例背景

12306 售票系统需要保证:

  • 同一车次余票的原子性操作:多个用户同时购票时,不能出现超卖(如余票为 1 时,两个用户同时抢到票)。
  • 分布式环境下的线程安全:多个售票服务节点(如不同服务器)同时处理请求时,需通过分布式锁协调。

实现步骤

1. 依赖引入
 <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>
2. ZooKeeper 连接配置
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;public class ZkClient {private static final String ZK_ADDRESS = "localhost:2181";private static CuratorFramework client;static {client = CuratorFrameworkFactory.newClient(ZK_ADDRESS,new ExponentialBackoffRetry(1000, 3));client.start();}public static CuratorFramework getClient() {return client;}
}
3. 票务服务(核心逻辑)
import org.apache.curator.framework.recipes.locks.InterProcessMutex;public class TicketService {private final InterProcessMutex lock;private int remainingTickets; // 剩余票数(模拟数据库)public TicketService(String lockPath, int initialTickets) {this.lock = new InterProcessMutex(ZkClient.getClient(), lockPath);this.remainingTickets = initialTickets;}// 购票方法public boolean buyTicket(String userId) {try {// 1. 获取分布式锁(阻塞式)if (lock.acquire(10, TimeUnit.SECONDS)) { // 超时时间10秒try {// 2. 检查余票(双重检查,避免锁内耗时)if (remainingTickets <= 0) {System.out.println("用户 " + userId + " 购票失败:票已售罄");return false;}// 3. 模拟业务逻辑(如扣减库存、生成订单)Thread.sleep(50); // 模拟网络延迟或耗时操作// 4. 扣减票数remainingTickets--;System.out.println("用户 " + userId + " 购票成功!剩余票数:" + remainingTickets);return true;} finally {// 5. 释放锁lock.release();}}} catch (Exception e) {e.printStackTrace();}return false;}
}
4. 模拟并发抢票
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class TicketSimulation {public static void main(String[] args) throws InterruptedException {// 初始化票务服务(锁路径为 /tickets/train123,初始票数10)TicketService ticketService = new TicketService("/tickets/train123", 10);// 模拟5个用户并发抢票ExecutorService executor = Executors.newFixedThreadPool(2);for (int i = 1; i <= 5; i++) {final String userId = "User-" + i;executor.execute(() -> ticketService.buyTicket(userId));}executor.shutdown();executor.awaitTermination(1, TimeUnit.MINUTES);ZkClient.getClient().close();}
}

关键点解析

  1. 分布式锁的作用

    • 确保同一时间只有一个线程能操作余票,避免超卖。
    • 使用 ZooKeeper 临时顺序节点实现,客户端宕机时锁自动释放。
  2. 锁的粒度

    • 锁路径 /tickets/train123 对应具体车次,不同车次互不影响。
  3. 超时控制

    • lock.acquire(10, TimeUnit.SECONDS) 防止死锁(如客户端崩溃未释放锁)。
  4. 双重检查

    • 获取锁后再次检查余票,避免锁内耗时导致其他线程重复扣减。
  5. 性能优化

    • 锁的持有时间尽可能短(仅包裹关键代码段)。

运行结果示例

用户 User-1 购票成功!剩余票数:9
用户 User-2 购票成功!剩余票数:8
...
用户 User-3 购票成功!剩余票数:0
用户 User-2 购票失败:票已售罄
...

扩展优化

  1. 数据库集成

    • 实际场景中,余票应存储在数据库,通过锁保证分布式事务(如扣减库存和生成订单的原子性)。
  2. 锁的公平性

    • Curator 的 InterProcessMutex 默认公平锁,按请求顺序获取锁。
  3. Redisson 替代方案

    • 如果使用 Redis,可用 Redisson 的 RLock 实现类似功能。
  4. 锁重试策略

    • 可通过 RetryNTimesRetryUntilElapsed 自定义重试逻辑。

通过 InterProcessMutex,12306 售票系统能安全处理高并发请求,确保数据一致性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/86877.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/86877.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设置方法区内存的大小

方法区内存配置 方法区&#xff08;Method Area&#xff09;是JVM内存模型的一部分&#xff0c;用于存储类信息、常量、静态变量等数据。在HotSpot虚拟机中&#xff0c;方法区的具体实现为永久代&#xff08;PermGen&#xff09;或元空间&#xff08;Metaspace&#xff09;&am…

用Flink打造实时数仓:生产环境中的“坑”与“解药”

目录 一、实时数仓的“野心”与“现实” 二、数据采集与接入:别让“源头”卡脖子 2.1 问题1:Kafka数据乱序与延迟 2.2 问题2:MySQL CDC数据同步异常 三、数据处理与计算:别让“算力”成瓶颈 3.1 问题3:多表Join性能低下 3.2 问题4:窗口计算触发延迟 四、状态管理与…

linux 下 Doris 单点部署

目录 1. Doris 下载 2. 环境准备 2.1 Linux 操作系统版本需求 2.2 部署依赖 3. Doris 部署 3.1 修改系统配置 3.1.1 修改系统句柄数 3.1.2 关闭swap分区 3.1.3 修改最大内存映射区域数量 3.2 开放端口 3.3 fe 部署 3.4 be 部署 3.5 be添加到Doris集群 4 验证 4.…

mysql 小版本升级实战分享

环境说明 当前版本:5.6.51 升级目标版本 mysql 5.7.41 服务启停通过systemd管理 升级准备&#xff1a; 环境检查 首先查看当前MySQL的版本信息&#xff0c;执行命令mysql -V&#xff0c;如图&#xff1a; 备份数据 备份所有数据库&#xff1a; 当数据量不是特别大的时候…

Python Ai语音识别教程

语音识别是将人类语音转换为文本的技术&#xff0c;在现代应用中非常有用。本教程将介绍如何使用Python实现基本的AI语音识别功能。 一、文字转语音 #文字转语音 #安装第三方库 pip install pyttsx3 #导包 &#xff1a; import pyttsx3import pyttsx3#创建语音引擎 a1 pytts…

Day11 制作窗口

文章目录 1. 显示窗口&#xff08;harib08d&#xff09;2. 消除闪烁1&#xff08;harib08g&#xff09;3. 消除闪烁2&#xff08;harib08h&#xff09; 本章的前三节做了如下修改&#xff1a; 解决了鼠标无法隐藏在屏幕右侧和下侧的问题。当鼠标隐藏在右侧时会在屏幕最左侧产生…

python+uniapp基于微信小程序蜀味道江湖餐饮管理系统nodejs+java

文章目录 具体实现截图本项目支持的技术路线源码获取详细视频演示&#xff1a;文章底部获取博主联系方式&#xff01;&#xff01;&#xff01;&#xff01;本系统开发思路进度安排及各阶段主要任务java类核心代码部分展示主要参考文献&#xff1a;源码获取/详细视频演示 ##项目…

postgresql增量备份系列二 pg_probackup

已经很久没有发文章了,主要是最近工作上的内容都不适合发文章公开。可能往后文章发表也不这么频繁了,不过大家有问题我们可以交流。之前有写过PG增量备份的其他工具使用方法,pg_probackup也是应用比较多的PG备份工具。 一. pg_probackup pg_probackup 是一个用于管理 Postg…

云手机主要是指什么?

云手机是指一种可以运行在云服务器中的手机&#xff0c;主要是将云计算技术运用于网络终端服务&#xff0c;通过云服务器来实现云服务的手机&#xff0c;也是一款深度结合了网络服务的手机&#xff0c;通过自带的系统和网络终端可以通过网络实现众多功能。 那么&#xff0c;下面…

CAU数据挖掘 支持向量机

SVM大致思想 线性分类问题 在一群点中用线性函数分类&#xff1a; 但也有线性不可分问题&#xff1a; 线性不可分问题&#xff1a; 最大间隔法 两个平行超平面间隔距离最大 软间隔 部分难以区分的点忽略 升维 通过升维将非线性变为线性 计算统计理论基础 学习过…

探索理解 Spring AI Advisors:构建可扩展的 AI 应用

Spring AI Advisors API 提供了一种灵活且强大的方式来拦截、修改和增强 Spring 应用程序中的 AI 驱动交互。其核心思想类似于 Spring AOP&#xff08;面向切面编程&#xff09;中的“通知”&#xff08;Advice&#xff09;&#xff0c;允许开发者在不修改核心业务逻辑的情况下…

Linux SSH服务全面配置指南:从基础到安全加固

Linux SSH服务全面配置指南&#xff1a;从基础到安全加固 概述 作为网络安全工程师&#xff0c;SSH&#xff08;Secure Shell&#xff09;服务的安全配置是我们日常工作中不可忽视的重要环节。本文将从基础配置到高级安全加固&#xff0c;全面解析SSH服务的各项参数&#xff…

.NET测试工具Parasoft dotTEST内置安全标准,编码合规更高效

在追求开发速度的时代&#xff0c;确保代码安全并满足严苛的行业合规标准如OWASP、CWE、PCI DSS、ISO 26262等已成为开发者的核心挑战&#xff0c;但开发人员常因复杂的编码标准和漏洞排查而效率低下。.NET测试工具Parasoft dotTEST内置安全标准&#xff0c;实现即插即用&#…

对象的finalization机制Test

Java语言提供了对象终止(finalization)机制来允许开发人员自定义对象被销毁之前的处理逻辑。当垃圾回收器发现没有引用指向一个对象时&#xff0c;通常接下来要做的就是垃圾回收&#xff0c;即清除该对象&#xff0c;而finalization机制使得在清除此对象之前&#xff0c;总会先…

AI初学者如何对大模型进行微调?——零基础保姆级实战指南

仅需8GB显存&#xff0c;三步完成个人专属大模型训练 四步实战&#xff1a;从环境配置到模型发布 步骤1&#xff1a;云端环境搭建&#xff08;10分钟&#xff09; 推荐使用阿里魔塔ModelScope免费GPU资源&#xff1a; # 注册后执行环境初始化 pip3 install --upgrade pip pi…

“单一职责”模式之装饰器模式

目录 “单一职责”模式装饰器模式 Decorator引例动机 Motivation模式定义结构 Structure要点总结 “单一职责”模式 在软件组件的设计中&#xff0c;如果责任划分的不清晰&#xff0c;使用继承得到的结果往往是随着需求的变化&#xff0c;子类急剧膨胀&#xff0c;同时充斥着重…

idea, CreateProcess error=206, 文件名或扩展名太长

idea, CreateProcess error206, 文件名或扩展名太长 解决 “CreateProcess error206, 文件名或扩展名太长” 错误 CreateProcess error206 是 Windows 系统特有的错误&#xff0c;表示命令行参数超出了 Windows 的 32767 字符限制。这个问题在 Java 开发中尤其常见&#xff0c…

一键高效率图片MD5修改工具PHP版

文章目录 图片MD5修改工具项目简介功能特点技术原理系统需求安装方法使用方法Web界面模式命令行模式文件结构常见问题注意事项开发者信息效果演示更多干货🎁1.如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “👍点赞” “✍️评论” “💙收藏” 一键三连哦!2.❤️…

跨主机用 Docker Compose 部署 PostgreSQL + PostGIS 主从

q下面是跨主机用 Docker Compose 部署 PostgreSQL PostGIS 主从复制的完整详细步骤&#xff08;主库 从库&#xff09;&#xff0c;主从都用官方 PostGIS 镜像 postgis/postgis:15-3.3&#xff0c;并注意网络与持久化。复制即可。 &#x1f6a9; 跨主机 PostgreSQL PostGIS …

会议动态|千眼狼高速摄像机、DIC测量系统等科学仪器亮相第十五届全国爆炸力学学术会议

第十五届全国爆炸力学学术会议于6月28日在绍兴盛大召开&#xff0c;会议汇聚来自全国爆炸力学与冲击领域专家学者2000余人&#xff0c;聚焦“爆炸与冲击动力学工程应用”、“材料动态力学行为与损伤断裂“、“工程爆破与毁伤评估”、“含能材料与水中爆炸”、“结构动态响应与安…