redisson分布式锁
This commit is contained in:
parent
dfa1adfd79
commit
9c4a9cf981
5
pom.xml
5
pom.xml
@ -701,6 +701,11 @@
|
||||
<artifactId>shedlock-provider-redis-spring</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
<version>3.16.1</version>
|
||||
</dependency>
|
||||
<!-- lettuce pool 缓存连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
||||
@ -2,6 +2,7 @@ package com;
|
||||
|
||||
|
||||
import cn.xuyanwu.spring.file.storage.EnableFileStorage;
|
||||
import com.zhgd.redis.lock.redisson.EnableDistributedLock;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.junit.Test;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
@ -35,6 +36,7 @@ import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
//@EnableConfigurationProperties({FileStorageProperties.class})
|
||||
@PropertySource(value = "classpath:application.properties", encoding = "utf-8")
|
||||
@SpringBootApplication
|
||||
@EnableDistributedLock
|
||||
public class WisdomSiteApplication extends SpringBootServletInitializer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
package com.zhgd.redis.lock.redisson;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface 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;
|
||||
}
|
||||
@ -0,0 +1,174 @@
|
||||
package com.zhgd.redis.lock.redisson;
|
||||
|
||||
import com.zhgd.jeecg.common.execption.OpenAlertException;
|
||||
import com.zhgd.jeecg.common.execption.OpenPromptException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.common.TemplateParserContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
|
||||
@Aspect
|
||||
@Slf4j
|
||||
public class DistributedLockAspect {
|
||||
|
||||
@Resource
|
||||
private IDistributedLock distributedLock;
|
||||
|
||||
/**
|
||||
* SpEL表达式解析
|
||||
*/
|
||||
private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
|
||||
|
||||
/**
|
||||
* 用于获取方法参数名字
|
||||
*/
|
||||
private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
@Pointcut("@annotation(com.zhgd.redis.lock.redisson.DistributedLock)")
|
||||
public void distributorLock() {
|
||||
}
|
||||
|
||||
@Around("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 OpenAlertException("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 OpenPromptException("正在处理中,请稍等");
|
||||
}
|
||||
|
||||
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 OpenAlertException("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 OpenAlertException("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 OpenAlertException("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 OpenAlertException("Invalid SpEL expression [" + spEL + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.zhgd.redis.lock.redisson;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import({DistributedLockAspect.class})
|
||||
public @interface EnableDistributedLock {
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
package com.zhgd.redis.lock.redisson;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
33
src/main/java/com/zhgd/redis/lock/redisson/ILock.java
Normal file
33
src/main/java/com/zhgd/redis/lock/redisson/ILock.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.zhgd.redis.lock.redisson;
|
||||
|
||||
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
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class ILock implements AutoCloseable {
|
||||
/**
|
||||
* 持有的锁对象
|
||||
*/
|
||||
@Getter
|
||||
private Object lock;
|
||||
/**
|
||||
* 分布式锁接口
|
||||
*/
|
||||
@Getter
|
||||
private IDistributedLock distributedLock;
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if(Objects.nonNull(lock)){
|
||||
distributedLock.unLock(lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
package com.zhgd.redis.lock.redisson;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RedissonDistributedLock implements IDistributedLock {
|
||||
|
||||
@Resource
|
||||
private RedissonClient redissonClient;
|
||||
/**
|
||||
* 统一前缀
|
||||
*/
|
||||
@Value("${redisson.lock.prefix:bi:distributed:lock}")
|
||||
private String prefix;
|
||||
|
||||
@Override
|
||||
public ILock lock(String key) {
|
||||
return this.lock(key, 0L, TimeUnit.SECONDS, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILock tryLock(String key, long tryTime) throws Exception {
|
||||
return this.tryLock(key, tryTime, 0L, TimeUnit.SECONDS, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@ package com.zhgd.xmgl.modules.quality.controller;
|
||||
|
||||
import com.zhgd.annotation.OperLog;
|
||||
import com.zhgd.jeecg.common.api.vo.Result;
|
||||
import com.zhgd.jeecg.common.mybatis.EntityMap;
|
||||
import com.zhgd.xmgl.modules.quality.entity.QualityRectifyRecord;
|
||||
import com.zhgd.xmgl.modules.quality.service.IQualityRectifyRecordService;
|
||||
import io.swagger.annotations.Api;
|
||||
@ -54,7 +53,7 @@ public class QualityRectifyRecordController {
|
||||
@OperLog(operModul = "质量管理", operType = "添加质量检查整改/复查记录信息", operDesc = "添加质量检查整改/复查记录信息")
|
||||
@ApiOperation(value = "添加质量检查-整改/复查记录信息", notes = "添加质量检查-整改/复查记录信息", httpMethod = "POST")
|
||||
@PostMapping(value = "/add")
|
||||
public Result<QualityRectifyRecord> add(@RequestBody QualityRectifyRecord qualityRectifyRecord) {
|
||||
public Result<QualityRectifyRecord> add(@RequestBody QualityRectifyRecord qualityRectifyRecord) throws Exception {
|
||||
qualityRectifyRecordService.saveQualityRectifyRecord(qualityRectifyRecord);
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ public interface IQualityRectifyRecordService extends IService<QualityRectifyRec
|
||||
|
||||
List<QualityRectifyRecord> selectRectifyRecordList(Map<String, Object> map);
|
||||
|
||||
void saveQualityRectifyRecord(QualityRectifyRecord qualityRectifyRecord);
|
||||
void saveQualityRectifyRecord(QualityRectifyRecord qualityRectifyRecord) throws Exception;
|
||||
|
||||
void qualityInspectionRecordRectifyExportExcel(Map<String, Object> map, HttpServletResponse response) throws IOException;
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.gexin.fastjson.JSON;
|
||||
import com.gexin.fastjson.JSONArray;
|
||||
import com.gexin.fastjson.JSONObject;
|
||||
import com.zhgd.redis.lock.redisson.DistributedLock;
|
||||
import com.zhgd.redis.lock.redisson.RedissonDistributedLock;
|
||||
import com.zhgd.xmgl.call.SanjiangDataCall;
|
||||
import com.zhgd.xmgl.modules.basicdata.service.INoticeService;
|
||||
import com.zhgd.xmgl.modules.quality.entity.QualityInspectionRecord;
|
||||
@ -68,8 +70,12 @@ public class QualityRectifyRecordServiceImpl extends ServiceImpl<QualityRectifyR
|
||||
return qualityRectifyRecordMapper.selectRectifyRecordList(map);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
RedissonDistributedLock redissonDistributedLock;
|
||||
|
||||
@Override
|
||||
public void saveQualityRectifyRecord(QualityRectifyRecord qualityRectifyRecord) {
|
||||
@DistributedLock(keyPrefix = "quality_rectify_record:",key = "#qualityRectifyRecord.qualityId",tryLok = true,lockTime = 0)
|
||||
public void saveQualityRectifyRecord(QualityRectifyRecord qualityRectifyRecord) throws Exception {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
qualityRectifyRecord.setCreateTime(sdf.format(new Date()));
|
||||
qualityRectifyRecordMapper.insert(qualityRectifyRecord);
|
||||
|
||||
@ -122,12 +122,20 @@ spring.redis.database=1
|
||||
spring.redis.host=127.0.0.1
|
||||
spring.redis.port=6379
|
||||
#spring.redis.password=JXJ@admin
|
||||
spring.redis.password=
|
||||
#分布式锁密码注释的时候,这个也要注释
|
||||
#spring.redis.password=
|
||||
spring.redis.timeout=2000s
|
||||
spring.redis.lettuce.pool.max-active=8
|
||||
spring.redis.lettuce.pool.max-wait=60s
|
||||
spring.redis.lettuce.pool.max-idle=10
|
||||
spring.redis.lettuce.pool.min-idle=10
|
||||
#分布式锁
|
||||
redisson.codec=org.redisson.codec.JsonJacksonCodec
|
||||
redisson.threads=4
|
||||
redisson.netty.threads=4
|
||||
redisson.single-server-config.address=redis://${spring.redis.host}:${spring.redis.port}
|
||||
#redisson.single-server-config.password=null
|
||||
#redisson.single-server-config.database=0
|
||||
# spring boot admin 所在服务器
|
||||
spring.boot.admin.client.url=http://localhost:9091
|
||||
# actuator 配置内容
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user