您的位置:首页 > 汽车 > 时评 > 集成分布式锁Redisson

集成分布式锁Redisson

2024/10/19 17:30:39 来源:https://blog.csdn.net/apple_74262176/article/details/141071223  浏览:    关键词:集成分布式锁Redisson

添加依赖

<!-- Redisson 分布式锁功能 -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.16.2</version>
</dependency>

添加配置

# 在nacos的pmhub-workflow中添加配置,配置 Redisson单机模式
redisson:codec: org.redisson.codec.JsonJacksonCodecthreads: 4netty:threads: 4single-server-config:address: "redis://localhost:6379"password: nulldatabase: 0

RedissonConfig

/*** @author canghe* @description RedissonConfig* @create 2024-06-18-16:36*/
@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient() {Config config = new Config();config.useSingleServer()// 如果redis部署在线上就填服务器IP地址.setAddress("redis://localhost:6379").setDatabase(0);return Redisson.create(config);}
}

定义ILock锁对象

package com.laigeoffer.pmhub.base.security.pojo;import com.laigeoffer.pmhub.base.security.service.redisson.IDistributedLock;
import lombok.AllArgsConstructor;
import lombok.Getter;import java.util.Objects;@AllArgsConstructor
public class ILock implements AutoCloseable {/*** 持有的锁对象*/@Getterprivate Object lock;/*** 分布式锁接口*/@Getterprivate IDistributedLock distributedLock;@Overridepublic void close() throws Exception {if (Objects.nonNull(lock)) {distributedLock.unLock(lock);}}
}

定义IDistributedLock分布式锁接口

import com.laigeoffer.pmhub.base.security.pojo.ILock;
import java.util.concurrent.TimeUnit;/*** @author canghe* @description IDistributedLock 分布式锁接口* @create 2024-06-17-10:23*/
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());}}}

定义IDistributedLock实现类

import com.laigeoffer.pmhub.base.security.pojo.ILock;
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;/*** @author canghe* @description RedissonDistributedLock 分布式锁实现类* @create 2024-06-17-10:31*/
@Slf4j
@Component
public class RedissonDistributedLock implements IDistributedLock {@Resourceprivate RedissonClient redissonClient;/*** 统一前缀*/@Value("${redisson.lock.prefix:bi:distributed:lock}")private String prefix;@Overridepublic ILock lock(String key) {return this.lock(key, 0L, TimeUnit.SECONDS, false);}@Overridepublic 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 秒续到30slock.lock();}return new ILock(lock, this);}@Overridepublic ILock tryLock(String key, long tryTime) throws Exception {return this.tryLock(key, tryTime, 0L, TimeUnit.SECONDS, false);}@Overridepublic 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 秒续到30slockAcquired = 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;}@Overridepublic 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);}}}
}

定义DistributedLock注解

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;/*** @author canghe* @description DistributedLock 分布式锁注解* @create 2024-06-17-10:16*/
@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;}

定义AOP切面控制

import com.laigeoffer.pmhub.base.core.exception.UtilException;
import com.laigeoffer.pmhub.base.core.utils.StringUtils;
import com.laigeoffer.pmhub.base.security.annotation.DistributedLock;
import com.laigeoffer.pmhub.base.security.pojo.ILock;
import com.laigeoffer.pmhub.base.security.service.redisson.IDistributedLock;
import lombok.extern.slf4j.Slf4j;
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 org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Objects;/*** @author canghe* @description DistributedLockAspect* @create 2024-06-17-10:20*/
@Aspect
@Slf4j
@Component
public class DistributedLockAspect {@Resourceprivate IDistributedLock distributedLock;/*** SpEL表达式解析*/private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();/*** 用于获取方法参数名字*/private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();@Pointcut("@annotation(com.laigeoffer.pmhub.base.security.annotation.DistributedLock)")public void distributorLock() {}@Around("distributorLock()")public Object around(ProceedingJoinPoint pjp) throws Throwable {// 获取DistributedLockDistributedLock distributedLock = this.getDistributedLock(pjp);// 获取 lockKeyString lockKey = this.getLockKey(pjp, distributedLock);ILock lockObj = null;try {// 加锁,tryLok = true,并且tryTime > 0时,尝试获取锁,获取不到超时异常if (distributedLock.tryLok()) {if(distributedLock.tryTime() <= 0){throw new UtilException("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 UtilException("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 UtilException("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 UtilException("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 UtilException("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 UtilException("Invalid SpEL expression [" + spEL + "]");}}
}

定义分布式锁启动元注解

import com.laigeoffer.pmhub.base.security.aspect.DistributedLockAspect;
import org.springframework.context.annotation.Import;import java.lang.annotation.*;/*** @author canghe* @description EnableDistributedLock 元注解,开启分布式锁功能* @create 2024-06-17-10:56*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({DistributedLockAspect.class})
public @interface EnableDistributedLock {
}

实战使用

更新项目和任务审批设置的时候,保证同一时刻只有一个服务能拿到锁

启动类上开启Redisson使用
@EnableCustomConfig
@EnablePmFeignClients
@EnableCustomSwagger2
@EnableDistributedLock // 启用Redisson分布式锁
@SpringBootApplication
public class PmHubWorkflowApplication {public static void main(String[] args) {SpringApplication.run(PmHubWorkflowApplication.class, args);}
}

使用DistributedLock注解
/*** 更新审批设置* @param approvalSetDTO* @return*/
@InnerAuth
@PostMapping("/updateApprovalSet")
@DistributedLock(key = "#approvalSetDTO.approved", lockTime = 10L, keyPrefix = "workflow-approve-")
public R<?> updateApprovalSet(ApprovalSetDTO approvalSetDTO) {return R.ok(deployService.updateApprovalSet(approvalSetDTO, ProjectStatusEnum.PROJECT.getStatusName()));
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com