Spring Boot 集成 Redisson分布式锁(注解版)
2024-02-07 17:40 l_v_y_forever 阅读(601) 评论(0) 编辑 收藏 举报转载自:https://blog.csdn.net/Ascend1977/article/details/131126047
Redisson 是一种基于 Redis 的 Java 驻留集群的分布式对象和服务库,可以为我们提供丰富的分布式锁和线程安全集合的实现。在 Spring Boot 应用程序中使用 Redisson 可以方便地实现分布式应用程序的某些方面,例如分布式锁、分布式集合、分布式事件发布和订阅等。本篇是一个使用 Redisson 实现分布式锁的详细示例,在这个示例中,我们定义了DistributedLock注解,它可以标注在方法上,配合DistributedLockAspect切面以及IDistributedLock分布式锁封装的接口,来实现redisson 分布式锁的 API 调用。
Spring Boot 集成 Redisson
1、在 pom.xml 文件中添加 Redisson 的相关依赖
-
<dependency>
-
<groupId>org.redisson</groupId>
-
<artifactId>redisson-spring-boot-starter</artifactId>
-
<version>3.16.1</version>
-
</dependency>
2、在 application.yml 中配置 Redisson单机模式 的连接信息和相关参数
-
spring:
-
redis:
-
host: localhost
-
port: 6379
-
password: null
-
redisson:
-
codec: org.redisson.codec.JsonJacksonCodec
-
threads: 4
-
netty:
-
threads: 4
-
single-server-config:
-
address: "redis://localhost:6379"
-
password: null
-
database: 0
3、Redission还支持主从、集群、哨兵配置
-
//主从模式
-
spring:
-
redis:
-
sentinel:
-
master: my-master
-
nodes: localhost:26379,localhost:26389
-
password: your_password
-
redisson:
-
master-slave-config:
-
master-address: "redis://localhost:6379"
-
slave-addresses: "redis://localhost:6380,redis://localhost:6381"
-
password: ${spring.redis.password}
-
-
// 集群模式
-
spring:
-
redis:
-
cluster:
-
nodes: localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384
-
password: your_password
-
redisson:
-
cluster-config:
-
node-addresses: "redis://localhost:6379,redis://localhost:6380,redis://localhost:6381,redis://localhost:6382,redis://localhost:6383,redis://localhost:6384"
-
password: ${spring.redis.password}
-
-
// 哨兵模式
-
spring:
-
redis:
-
sentinel:
-
master: my-master
-
nodes: localhost:26379,localhost:26389
-
password: your_password
-
redisson:
-
sentinel-config:
-
master-name: my-master
-
sentinel-addresses: "redis://localhost:26379,redis://localhost:26380,redis://localhost:26381"
-
password: ${spring.redis.password}
-
本地封装Redisson 分布式锁
1、定义IDistributedLock分布式锁接口
-
public interface IDistributedLock {
-
/**
-
* 获取锁,默认30秒失效,失败一直等待直到获取锁
-
*
-
* @param key 锁的key
-
* @return 锁对象
-
*/
-
ILock lock(String key);
-
-
/**
-
* 获取锁,失败一直等待直到获取锁
-
*
-
* @param key 锁的key
-
* @param lockTime 加锁的时间,超过这个时间后锁便自动解锁; 如果lockTime为-1,则保持锁定直到显式解锁
-
* @param unit {@code lockTime} 参数的时间单位
-
* @param fair 是否公平锁
-
* @return 锁对象
-
*/
-
ILock lock(String key, long lockTime, TimeUnit unit, boolean fair);
-
-
/**
-
* 尝试获取锁,30秒获取不到超时异常,锁默认30秒失效
-
*
-
* @param key 锁的key
-
* @param tryTime 获取锁的最大尝试时间
-
* @return
-
* @throws Exception
-
*/
-
ILock tryLock(String key, long tryTime) throws Exception;
-
-
/**
-
* 尝试获取锁,获取不到超时异常
-
*
-
* @param key 锁的key
-
* @param tryTime 获取锁的最大尝试时间
-
* @param lockTime 加锁的时间
-
* @param unit {@code tryTime @code lockTime} 参数的时间单位
-
* @param fair 是否公平锁
-
* @return
-
* @throws Exception
-
*/
-
ILock tryLock(String key, long tryTime, long lockTime, TimeUnit unit, boolean fair) throws Exception;
-
-
/**
-
* 解锁
-
*
-
* @param lock
-
* @throws Exception
-
*/
-
void unLock(Object lock);
-
-
-
/**
-
* 释放锁
-
*
-
* @param lock
-
* @throws Exception
-
*/
-
default void unLock(ILock lock) {
-
if (lock != null) {
-
unLock(lock.getLock());
-
}
-
}
-
-
-
}
2、IDistributedLock实现类
-
-
-
public class RedissonDistributedLock implements IDistributedLock {
-
-
-
private RedissonClient redissonClient;
-
/**
-
* 统一前缀
-
*/
-
-
private String prefix;
-
-
-
public ILock lock(String key) {
-
return this.lock(key, 0L, TimeUnit.SECONDS, false);
-
}
-
-
-
public ILock lock(String key, long lockTime, TimeUnit unit, boolean fair) {
-
RLock lock = getLock(key, fair);
-
// 获取锁,失败一直等待,直到获取锁,不支持自动续期
-
if (lockTime > 0L) {
-
lock.lock(lockTime, unit);
-
} else {
-
// 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30s
-
lock.lock();
-
}
-
return new ILock(lock, this);
-
}
-
-
-
public ILock tryLock(String key, long tryTime) throws Exception {
-
return this.tryLock(key, tryTime, 0L, TimeUnit.SECONDS, false);
-
}
-
-
-
public ILock tryLock(String key, long tryTime, long lockTime, TimeUnit unit, boolean fair)
-
throws Exception {
-
RLock lock = getLock(key, fair);
-
boolean lockAcquired;
-
// 尝试获取锁,获取不到超时异常,不支持自动续期
-
if (lockTime > 0L) {
-
lockAcquired = lock.tryLock(tryTime, lockTime, unit);
-
} else {
-
// 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30s
-
lockAcquired = lock.tryLock(tryTime, unit);
-
}
-
if (lockAcquired) {
-
return new ILock(lock, this);
-
}
-
return null;
-
}
-
-
/**
-
* 获取锁
-
*
-
* @param key
-
* @param fair
-
* @return
-
*/
-
private RLock getLock(String key, boolean fair) {
-
RLock lock;
-
String lockKey = prefix + ":" + key;
-
if (fair) {
-
// 获取公平锁
-
lock = redissonClient.getFairLock(lockKey);
-
} else {
-
// 获取普通锁
-
lock = redissonClient.getLock(lockKey);
-
}
-
return lock;
-
}
-
-
-
public void unLock(Object lock) {
-
if (!(lock instanceof RLock)) {
-
throw new IllegalArgumentException("Invalid lock object");
-
}
-
RLock rLock = (RLock) lock;
-
if (rLock.isLocked()) {
-
try {
-
rLock.unlock();
-
} catch (IllegalMonitorStateException e) {
-
log.error("释放分布式锁异常", e);
-
}
-
}
-
}
-
}
3、定义ILock锁对象
-
import lombok.AllArgsConstructor;
-
import lombok.Getter;
-
-
import java.util.Objects;
-
-
/**
-
* <p>
-
* RedissonLock 包装的锁对象 实现AutoCloseable接口,在java7的try(with resource)语法,不用显示调用close方法
-
* </p>
-
-
* @since 2023-06-08 16:57
-
*/
-
-
public class ILock implements AutoCloseable {
-
/**
-
* 持有的锁对象
-
*/
-
-
private Object lock;
-
/**
-
* 分布式锁接口
-
*/
-
-
private IDistributedLock distributedLock;
-
-
-
public void close() throws Exception {
-
if(Objects.nonNull(lock)){
-
distributedLock.unLock(lock);
-
}
-
}
-
}
4、注入IDistributedLock接口使用示例
-
// 定义接口
-
public interface IProductSkuSupplierMeasureService {
-
/**
-
* 保存SKU供应商供货信息
-
*
-
* @param dto
-
* @return
-
*
-
Boolean saveSupplierInfo(ProductSkuSupplierInfoDTO dto);
-
-
/**
-
* 编辑SKU供应商供货信息
-
*
-
* @param dto
-
* @return
-
*/
-
Boolean editSupplierInfo(ProductSkuSupplierInfoDTO dto);
-
}
-
-
手动释放锁示例
-
// 实现类
-
-
public class ProductSkuSupplierMeasureServiceImpl
-
implements IProductSkuSupplierMeasureService {
-
-
-
private IDistributedLock distributedLock;
-
-
-
-
public Boolean saveSupplierInfo(ProductSkuSupplierInfoDTO dto) {
-
// 手动释放锁
-
String sku = dto.getSku();
-
ILock lock = null;
-
try {
-
lock = distributedLock.lock(dto.getSku(),10L, TimeUnit.SECONDS, false);
-
if (Objects.isNull(lock)) {
-
throw new BusinessException("Duplicate request for method still in process");
-
}
-
// 业务代码
-
}catch (BusinessException e) {
-
throw new BusinessException(e.getMessage());
-
} catch (Exception e) {
-
log.error("保存异常", e);
-
throw new BusinessException (e.getMessage());
-
} finally {
-
if (Objects.nonNull(lock)) {
-
distributedLock.unLock(lock);
-
}
-
}
-
return Boolean.TRUE;
-
}
-
-
}
使用try-with-resources 语法糖自动释放锁
-
// 实现类
-
-
public class ProductSkuSupplierMeasureServiceImpl
-
implements IProductSkuSupplierMeasureService {
-
-
-
private IDistributedLock distributedLock;
-
-
-
-
-
public Boolean editSupplierInfo(ProductSkuSupplierInfoDTO dto) {
-
-
String sku = dto.getSku();
-
// try-with-resources 语法糖自动释放锁
-
try(ILock lock = distributedLock.lock(dto.getSku(),10L, TimeUnit.SECONDS, false)) {
-
if(Objects.isNull(lock)){
-
throw new BusinessException ("Duplicate request for method still in process");
-
}
-
-
// 业务代码
-
}catch (BusinessException e) {
-
throw new BusinessException (e.getMessage());
-
} catch (Exception e) {
-
log.error("修改异常", e);
-
throw new BusinessException ("修改异常");
-
}
-
return Boolean.TRUE;
-
}
-
}
5、使用AOP切面实现分布式锁的绑定
定义DistributedLock注解
-
-
-
-
public DistributedLock {
-
-
/**
-
* 保证业务接口的key的唯一性,否则失去了分布式锁的意义 锁key
-
* 支持使用spEl表达式
-
*/
-
String key();
-
-
/**
-
* 保证业务接口的key的唯一性,否则失去了分布式锁的意义 锁key 前缀
-
*/
-
String keyPrefix() default "";
-
-
/**
-
* 是否在等待时间内获取锁,如果在等待时间内无法获取到锁,则返回失败
-
*/
-
boolean tryLok() default false;
-
-
/**
-
* 获取锁的最大尝试时间 ,会尝试tryTime时间获取锁,在该时间内获取成功则返回,否则抛出获取锁超时异常,tryLok=true时,该值必须大于0。
-
*
-
*/
-
long tryTime() default 0;
-
-
/**
-
* 加锁的时间,超过这个时间后锁便自动解锁
-
*/
-
long lockTime() default 30;
-
-
/**
-
* tryTime 和 lockTime的时间单位
-
*/
-
TimeUnit unit() default TimeUnit.SECONDS;
-
-
/**
-
* 是否公平锁,false:非公平锁,true:公平锁
-
*/
-
boolean fair() default false;
-
}
定义DistributedLockAspect Lock切面
-
-
-
public class DistributedLockAspect {
-
-
-
private IDistributedLock distributedLock;
-
-
/**
-
* SpEL表达式解析
-
*/
-
private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
-
-
/**
-
* 用于获取方法参数名字
-
*/
-
private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
-
-
-
public void distributorLock() {
-
}
-
-
-
public Object around(ProceedingJoinPoint pjp) throws Throwable {
-
// 获取DistributedLock
-
DistributedLock distributedLock = this.getDistributedLock(pjp);
-
// 获取 lockKey
-
String lockKey = this.getLockKey(pjp, distributedLock);
-
ILock lockObj = null;
-
try {
-
// 加锁,tryLok = true,并且tryTime > 0时,尝试获取锁,获取不到超时异常
-
if (distributedLock.tryLok()) {
-
if(distributedLock.tryTime() <= 0){
-
throw new IdempotencyException("tryTime must be greater than 0");
-
}
-
lockObj = this.distributedLock.tryLock(lockKey, distributedLock.tryTime(), distributedLock.lockTime(), distributedLock.unit(), distributedLock.fair());
-
} else {
-
lockObj = this.distributedLock.lock(lockKey, distributedLock.lockTime(), distributedLock.unit(), distributedLock.fair());
-
}
-
-
if (Objects.isNull(lockObj)) {
-
throw new IdempotencyException("Duplicate request for method still in process");
-
}
-
-
return pjp.proceed();
-
} catch (Exception e) {
-
throw e;
-
} finally {
-
// 解锁
-
this.unLock(lockObj);
-
}
-
}
-
-
/**
-
* @param pjp
-
* @return
-
* @throws NoSuchMethodException
-
*/
-
private DistributedLock getDistributedLock(ProceedingJoinPoint pjp) throws NoSuchMethodException {
-
String methodName = pjp.getSignature().getName();
-
Class clazz = pjp.getTarget().getClass();
-
Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
-
Method lockMethod = clazz.getMethod(methodName, par);
-
DistributedLock distributedLock = lockMethod.getAnnotation(DistributedLock.class);
-
return distributedLock;
-
}
-
-
/**
-
* 解锁
-
*
-
* @param lockObj
-
*/
-
private void unLock(ILock lockObj) {
-
if (Objects.isNull(lockObj)) {
-
return;
-
}
-
-
try {
-
this.distributedLock.unLock(lockObj);
-
} catch (Exception e) {
-
log.error("分布式锁解锁异常", e);
-
}
-
}
-
-
/**
-
* 获取 lockKey
-
*
-
* @param pjp
-
* @param distributedLock
-
* @return
-
*/
-
private String getLockKey(ProceedingJoinPoint pjp, DistributedLock distributedLock) {
-
String lockKey = distributedLock.key();
-
String keyPrefix = distributedLock.keyPrefix();
-
if (StringUtils.isBlank(lockKey)) {
-
throw new IdempotencyException("Lok key cannot be empty");
-
}
-
if (lockKey.contains("#")) {
-
this.checkSpEL(lockKey);
-
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
-
// 获取方法参数值
-
Object[] args = pjp.getArgs();
-
lockKey = getValBySpEL(lockKey, methodSignature, args);
-
}
-
lockKey = StringUtils.isBlank(keyPrefix) ? lockKey : keyPrefix + lockKey;
-
return lockKey;
-
}
-
-
/**
-
* 解析spEL表达式
-
*
-
* @param spEL
-
* @param methodSignature
-
* @param args
-
* @return
-
*/
-
private String getValBySpEL(String spEL, MethodSignature methodSignature, Object[] args) {
-
// 获取方法形参名数组
-
String[] paramNames = nameDiscoverer.getParameterNames(methodSignature.getMethod());
-
if (paramNames == null || paramNames.length < 1) {
-
throw new IdempotencyException("Lok key cannot be empty");
-
}
-
Expression expression = spelExpressionParser.parseExpression(spEL);
-
// spring的表达式上下文对象
-
EvaluationContext context = new StandardEvaluationContext();
-
// 给上下文赋值
-
for (int i = 0; i < args.length; i++) {
-
context.setVariable(paramNames[i], args[i]);
-
}
-
Object value = expression.getValue(context);
-
if (value == null) {
-
throw new IdempotencyException("The parameter value cannot be null");
-
}
-
return value.toString();
-
}
-
-
/**
-
* SpEL 表达式校验
-
*
-
* @param spEL
-
* @return
-
*/
-
private void checkSpEL(String spEL) {
-
try {
-
ExpressionParser parser = new SpelExpressionParser();
-
parser.parseExpression(spEL, new TemplateParserContext());
-
} catch (Exception e) {
-
log.error("spEL表达式解析异常", e);
-
throw new IdempotencyException("Invalid SpEL expression [" + spEL + "]");
-
}
-
}
-
}
定义分布式锁注解版启动元注解
-
-
-
-
-
public EnableDistributedLock {
-
}
6、AOP切面实现分布式锁的绑定使用示例
启动类添加@EnableDistributedLock启用注解支持
-
-
-
public class BiCenterGoodsApplication {
-
-
public static void main(String[] args) {
-
-
SpringApplication.run(BiCenterGoodsApplication.class, args);
-
-
}
-
}
@DistributedLock标注需要使用分布式锁的方法
-
-
-
-
//@DistributedLock(key = "#dto.sku + '-' + #dto.skuId", lockTime = 10L, keyPrefix = "sku-")
-
-
public R<Boolean> editSupplierInfo( { ProductSkuSupplierInfoDTO dto)
-
return R.ok(productSkuSupplierMeasureService.editSupplierInfo(dto));
-
}
#dto.sku是 SpEL表达式。Spring中支持的它都支持的。比如调用静态方法,三目表达式。SpEL 可以使用方法中的任何参数。SpEL表达式参考
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通