Spring Boot Starter实现基于 Redis + Lua 的分布式锁封装与实践
Spring Boot Starter实现基于 Redis Lua 的分布式锁封装与实践一、涉及的技术知识点1.1 Redis 分布式锁核心原理知识点说明Redis 单线程模型保证命令按序执行天然支持原子操作SETNXSET if Not eXists只有 key 不存在时才设置成功是分布式锁的基础PEXPIRE设置 key 毫秒级过期时间防止死锁HSET/HGET用 Hash 结构存储锁持有者标识requestId支持安全释放Lua 脚本多个 Redis 命令在服务端原子执行保证加锁/解锁操作的原子性锁超时自动释放持有者崩溃时锁不会永久占用可重入防护通过 requestId 标识持有者只有持有者能释放锁1.2 Spring Boot 自动配置机制知识点说明spring.factoriesSpring Boot SPI 加载机制声明自动配置类ConfigurationBean声明式注册 Spring BeanAutowired/Resource自动注入StringRedisTemplate条件装配可配合ConditionalOnClass实现按需加载1.3 Java 语言特性知识点说明Closeable接口实现自动资源管理支持 try-with-resourcesLambda 表达式RedisCallback用 Lambda 简化回调写法自旋等待 超时退出tryLock内循环重试直到超时包级访问控制构造器和 setter 使用包级权限只允许通过 Provider 创建1.4 设计模式模式应用工厂模式DistributedLockProvider作为工厂创建DistributedLock实例模板方法StringRedisTemplate.execute(RedisCallback)统一连接管理策略模式不同TimeUnitleaseTime组合适应不同场景注博客https://blog.csdn.net/badao_liumang_qizhi二、封装 Spring Boot Starter JAR 包的流程2.1 项目结构xxx-xxx-lib-lock/ ├── pom.xml └── src/main/ ├── java/xx/xx/lib/lock/ │ ├── LockConfig.java // 自动配置类 │ ├── DistributedLockProvider.java // 锁工厂对外暴露 │ └── DistributedLock.java // 锁实现核心逻辑 └── resources/META-INF/ └── spring.factories // 自动配置声明2.2 封装步骤定义核心接口/类— 确定对外暴露的 APIProvider Lock实现核心逻辑— Redis Lua 脚本加锁解锁编写自动配置—Configuration类注册 Bean声明 spring.factories— 让 Spring Boot 自动发现配置类Maven 打包发布—mvn deploy到私有仓库2.3 引入方使用流程pom.xml添加依赖确保项目已配置spring.redisStringRedisTemplate可用注入DistributedLockProvider直接使用无需任何额外配置或注解开启三、通用示例代码3.1 Starter 封装侧pom.xmlprojectgroupIdcom.example/groupIdartifactIdexample-distributed-lock-starter/artifactIdversion1.0.0/versionpackagingjar/packagingdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactIdscopeprovided/scope/dependencydependencygroupIdorg.slf4j/groupIdartifactIdslf4j-api/artifactIdscopeprovided/scope/dependency/dependencies/projectDistributedLock.javapackagecom.example.lock;importjava.io.Closeable;importjava.util.concurrent.TimeUnit;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.data.redis.core.StringRedisTemplate;/** * 基于Redis的分布式锁实现. * 支持自旋等待、超时释放、安全解锁. */publicclassDistributedLockimplementsCloseable{privatestaticfinalLoggerlogLoggerFactory.getLogger(DistributedLock.class);privatestaticfinalStringLOCK_PREFIXapp-locks:{%s};privatestaticfinalStringREQUEST_ID_KEYrid-{%s};/** 加锁Lua脚本key不存在时设置hash并设过期时间. */privatestaticfinalStringLOCK_SCRIPTif (redis.call(exists, KEYS[1]) 0) then redis.call(hset, KEYS[1], KEYS[2], ARGV[1]); redis.call(pexpire, KEYS[1], ARGV[2]); return 1; else return 0; end;/** 解锁Lua脚本校验requestId匹配后删除key. */privatestaticfinalStringUNLOCK_SCRIPTif redis.call(hget, KEYS[1], KEYS[2]) ARGV[1] then redis.call(del, KEYS[1]); return 1; else return 0; end;;privatefinalStringRedisTemplateredisTemplate;privatefinalStringkey;privatefinalStringlockKey;privatefinalStringrequestIdKey;privatefinalStringrequestId;privateTimeUnitleaseTimeUnitTimeUnit.MINUTES;privatelongleaseTime10;privatebooleanlockedfalse;DistributedLock(StringRedisTemplateredisTemplate,Stringkey,StringrequestId){this.redisTemplateredisTemplate;this.keykey;this.requestId(requestIdnull||requestId.isEmpty())?java.util.UUID.randomUUID().toString().replace(-,):requestId;this.lockKeyString.format(LOCK_PREFIX,key);this.requestIdKeyString.format(REQUEST_ID_KEY,key);}voidsetLeaseTimeUnit(TimeUnitleaseTimeUnit){if(leaseTimeUnit!null){this.leaseTimeUnitleaseTimeUnit;}}voidsetLeaseTime(longleaseTime){if(leaseTime0){this.leaseTimeleaseTime;}}/** * 尝试获取锁支持超时等待. * * param waitTimeUnit 等待时间单位 * param waitTime 最大等待时间 * return true-获取成功false-超时未获取到 */publicbooleantryLock(TimeUnitwaitTimeUnit,longwaitTime){BooleanresultredisTemplate.execute(connection-{longwaitMilliswaitTimeUnit.toMillis(waitTime);longstartTimeSystem.currentTimeMillis();longdeadlinestartTimewaitMillis;// 第一次尝试if(doLock(connection)){returnBoolean.TRUE;}// 自旋重试直到超时while(System.currentTimeMillis()deadline){if(doLock(connection)){returnBoolean.TRUE;}try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedExceptione){log.error(获取锁等待时被中断,e);Thread.currentThread().interrupt();returnBoolean.FALSE;}}returnBoolean.FALSE;});this.lockedBoolean.TRUE.equals(result);log.debug(获取锁结果 - {} - {},key,locked);returnthis.locked;}/** * 释放锁. * * return true-释放成功false-释放失败非持有者或已过期 */publicbooleanunlock(){if(!locked){log.info(未锁无需操作 - {},key);returntrue;}log.info(准备释放锁 - {},key);BooleanresultredisTemplate.execute(connection-{longexpireMillisleaseTimeUnit.toMillis(leaseTime);LongevalResult(Long)connection.eval(UNLOCK_SCRIPT.getBytes(),org.springframework.data.redis.connection.ReturnType.INTEGER,2,lockKey.getBytes(),requestIdKey.getBytes(),requestId.getBytes());log.info(释放锁数量 - {} - {} - {},requestId,key,evalResult);returnevalResult!nullevalResult!0?Boolean.TRUE:Boolean.FALSE;});this.locked!Boolean.TRUE.equals(result);returnBoolean.TRUE.equals(result);}Overridepublicvoidclose(){unlock();}privatebooleandoLock(org.springframework.data.redis.connection.RedisConnectionconnection){longexpireMillisleaseTimeUnit.toMillis(leaseTime);LongevalResult(Long)connection.eval(LOCK_SCRIPT.getBytes(),org.springframework.data.redis.connection.ReturnType.INTEGER,2,lockKey.getBytes(),requestIdKey.getBytes(),requestId.getBytes(),String.valueOf(expireMillis).getBytes());returnevalResult!nullevalResult!0;}}DistributedLockProvider.javapackagecom.example.lock;importjava.util.concurrent.TimeUnit;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.StringRedisTemplate;/** * 分布式锁工厂. * 通过注入此类获取锁实例. */publicclassDistributedLockProvider{AutowiredprivateStringRedisTemplateredisTemplate;/** * 获取分布式锁默认过期10分钟. * * param key 业务锁标识 * return 锁实例 */publicDistributedLockgetLock(Stringkey){returnnewDistributedLock(redisTemplate,key,getCurrentRequestId());}/** * 获取分布式锁自定义过期时间. * * param key 业务锁标识 * param leaseTimeUnit 过期时间单位 * param leaseTime 过期时间值 * return 锁实例 */publicDistributedLockgetLock(Stringkey,TimeUnitleaseTimeUnit,longleaseTime){DistributedLocklocknewDistributedLock(redisTemplate,key,getCurrentRequestId());lock.setLeaseTime(leaseTime);lock.setLeaseTimeUnit(leaseTimeUnit);returnlock;}privateStringgetCurrentRequestId(){// 从链路追踪上下文获取requestId保证同一请求内的锁标识一致// 实际项目中可替换为 MDC.get(requestId) 或自定义实现returnjava.util.UUID.randomUUID().toString().replace(-,);}}LockConfig.javapackagecom.example.lock;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;/** * 分布式锁自动配置类. */ConfigurationpublicclassLockConfig{BeanpublicDistributedLockProviderdistributedLockProvider(){returnnewDistributedLockProvider();}}META-INF/spring.factories# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration\ com.example.lock.LockConfig3.2 引入方使用侧pom.xml 添加依赖dependencygroupIdcom.example/groupIdartifactIdexample-distributed-lock-starter/artifactIdversion1.0.0/version/dependencyapplication.yml确保 Redis 已配置spring:redis:host:localhostport:6379password:your_passworddatabase:0使用示例1try-with-resources 自动释放最佳实践ServicepublicclassOrderService{ResourceprivateDistributedLockProviderdistributedLockProvider;publicvoidprocessOrder(StringorderId){StringlockKeyorder_process_orderId;// 锁过期5分钟等待获取锁最多3分钟try(DistributedLocklockdistributedLockProvider.getLock(lockKey,TimeUnit.MINUTES,5)){if(lock.tryLock(TimeUnit.MINUTES,3)){// 获取锁成功安全执行业务逻辑doProcessOrder(orderId);}else{// 超时未获取到锁thrownewRuntimeException(订单正在处理中请勿重复操作);}}// try 块结束自动调用 close() - unlock()}}使用示例2非阻塞立即返回publicvoidsubmitTask(StringtaskId){StringlockKeytask_submit_taskId;try(DistributedLocklockdistributedLockProvider.getLock(lockKey)){// 等待时间为0立即返回结果if(lock.tryLock(TimeUnit.MILLISECONDS,0)){executeTask(taskId);}else{thrownewRuntimeException(任务已在执行中);}}}使用示例3事务提交后释放锁TransactionalpublicvoidupdateStock(IntegeritemId){Stringkeystock_update_itemId;DistributedLocklockdistributedLockProvider.getLock(key,TimeUnit.MINUTES,3);if(!lock.tryLock(TimeUnit.MINUTES,1)){thrownewRuntimeException(库存操作冲突);}// 在事务提交后再释放锁保证其他线程读到的是已提交数据TransactionSynchronizationManager.registerSynchronization(newTransactionSynchronization(){OverridepublicvoidafterCommit(){lock.unlock();}});// 执行库存扣减doUpdateStock(itemId);}使用示例4MQ 消费防重复处理ComponentpublicclassOrderMqConsumer{ResourceprivateDistributedLockProviderdistributedLockProvider;publicvoidonMessage(MapString,Objectmessage){StringorderId(String)message.get(orderId);StringlockKeymq_consume_order_orderId;// 锁10分钟等待8分钟try(DistributedLocklockdistributedLockProvider.getLock(lockKey,TimeUnit.MINUTES,10)){if(lock.tryLock(TimeUnit.MINUTES,8)){processMessage(message);}else{log.warn(消息正在被其他消费者处理: {},orderId);}}}}四、设计要点总结要点实现方式收益零配置使用spring.factories 自动装配引入依赖即可用无需Enable注解防死锁锁自带过期时间进程崩溃不会永久阻塞防误释放requestId 校验A 加的锁不会被 B 释放原子操作Lua 脚本避免 check-then-act 竞态条件资源安全Closeable try-with-resources确保锁一定被释放灵活度工厂模式 可配参数不同场景可设不同超时可观测性关键操作打日志方便排查锁竞争问题最小依赖仅依赖 spring-data-redis slf4j轻量无侵入

相关新闻

Kiro Steering 文件 Inclusion 模式详解

Kiro Steering 文件 Inclusion 模式详解

Kiro Steering 文件 Inclusion 模式详解 一、什么是 Steering 文件 Steering 文件是 Kiro 的上下文注入机制——通过 Markdown 文件向 Kiro 提供额外的指令、规范和上下文信息,影响 Kiro 在对话中的行为方式。 它的作用类似于给 Kiro 制定"工作手册"&…

2026/7/24 2:00:28阅读更多 →
BiSeNet:实时语义分割的双边网络架构解析

BiSeNet:实时语义分割的双边网络架构解析

1. BiSeNet项目概述在计算机视觉领域,实时语义分割一直是个极具挑战性的任务。传统方法要么追求精度牺牲速度,要么注重速度却丢失细节。2018年提出的BiSeNet(Bilateral Segmentation Network)通过创新的双边结构设计,成…

2026/7/24 2:00:28阅读更多 →
相机标定技术详解:从原理到实践

相机标定技术详解:从原理到实践

1. 相机标定基础概念解析相机标定是计算机视觉领域的基础性工作,它的核心目标是确定相机成像的几何模型参数。简单来说,就像给相机做"体检",通过测量获得它的"视力参数"。这些参数主要包括内参(焦距、主点坐标…

2026/7/24 2:00:28阅读更多 →
提示工程ROI评估框架与实践指南

提示工程ROI评估框架与实践指南

1. 提示工程ROI评估的核心框架在人工智能应用领域,提示工程(Prompt Engineering)已经从单纯的技巧演变为影响项目成败的关键因素。但很多团队在投入提示工程优化时,常常陷入"投入越多效果越好"的误区,缺乏系…

2026/7/24 3:32:58阅读更多 →
意图路由架构优化LLM应用性能与成本

意图路由架构优化LLM应用性能与成本

1. 项目概述:意图路由如何重塑LLM应用架构最近半年在落地多个企业级大语言模型项目时,我发现一个共性痛点:当业务场景从简单的问答机器人扩展到复杂工作流时,传统的全量检索(Full Retrieval)方案会导致响应…

2026/7/24 3:32:58阅读更多 →
银行智能体构建:OpenClaw+DIFY+RAG技术解析与应用

银行智能体构建:OpenClaw+DIFY+RAG技术解析与应用

1. 银行智能体构建的技术背景与核心价值在金融数字化转型浪潮中,银行智能体正从简单的问答机器人进化为具备复杂决策能力的数字员工。OpenClawDIFYRAG的技术组合为银行业务场景提供了端到端的智能体构建方案,其核心价值体现在三个维度:业务响…

2026/7/24 3:32:58阅读更多 →
从零到一,把拓扑排序“拆“给你看

从零到一,把拓扑排序“拆“给你看

很多人第一次接触「拓扑排序」,会被名字唬住 —— 听起来像是某种复杂的数值排序算法。但剥开概念外壳,它的本质极其朴素:给一组有依赖关系的任务,排出一个合法的执行顺序。所有前置任务必须排在后面任务的前面,不能出…

2026/7/24 3:32:58阅读更多 →
植被参数光学遥感反演方法(Python)及遥感与生态模型数据同化算法实践技术

植被参数光学遥感反演方法(Python)及遥感与生态模型数据同化算法实践技术

查看原文>>>https://mp.weixin.qq.com/s/-LCo0FaDKKBo92xnjYwe-Q 目录 内容简述 专题一、植被参数遥感反演理论 专题二、植被叶片及冠层反射率模拟与处理 专题三、植被遥感模型参数敏感性分析 专题四、基于查找表(LUT)方法反演植被参数 专题五、基于优化算法反…

2026/7/24 3:32:58阅读更多 →
基于多模态AI与YOLO的太阳能电池板缺陷检测系统

基于多模态AI与YOLO的太阳能电池板缺陷检测系统

1. 项目概述:太阳能电池板缺陷检测的智能化升级光伏电站运维人员每天需要巡检数千块太阳能电池板,传统人工检测方式存在效率低、漏检率高的问题。我们团队开发的这套系统,首次将多模态AI大模型与YOLO系列目标检测算法结合,实现了对…

2026/7/24 3:30:58阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 0:58:53阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 0:58:53阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 0:58:53阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:06阅读更多 →
【LeetCode 54】螺旋矩阵

【LeetCode 54】螺旋矩阵

问题描述: 解法: 1、模拟(参考自【LeetCode 54】螺旋矩阵-CSDN博客) int *spiralOrder(int **matrix, int matrixSize, int *matrixColSize, int *returnSize) {static const int dirs[4][2] {{0, 1}, {1, 0}, {0, -1}, {-1, …

2026/7/24 0:00:06阅读更多 →
2026 WAIC:模型隐身、智能体疯野,厂商竞赛聚焦办公场景与商业闭环

2026 WAIC:模型隐身、智能体疯野,厂商竞赛聚焦办公场景与商业闭环

知春路不相信模型领先今年WAIC大会,昔日AI六小龙来了五家,分别是Kimi、阶跃星辰、Minimax、百川智能、零一万物。连放弃基模的百川和零一万物都来了,唯一缺席的竟是近几个月来风光无限的智谱。(DeepSeek一直不参加)WAI…

2026/7/24 0:00:06阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/23 22:58:43阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/23 18:58:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/23 18:58:18阅读更多 →