【分布式】基于Redisson实现对分布式锁的注解式封装
基于Redisson实现对分布式锁的注解式封装
- 实现结果
- 实现@DistributeLock注解
- 实现分布式锁切面
实现结果
可以基于注解的方式给一个方法体添加分布式锁 并实现自动释放
示例
@Service
public class OrderService {@DistributeLock(scene = "order",keyExpression = "#orderId",expireTime = 3000,waitTime = 1000)public void createOrder(String orderId, User user) {// 处理订单创建逻辑,自动加锁/解锁// 锁键为 "order#123"(假设 orderId=123)}@DistributeLock(scene = "stock",key = "product_#productId",expireTime = 5000)public void deductStock(Long productId, int quantity) {// 锁键为 "stock#product_1001"(假设 productId=1001)}
}
实现@DistributeLock注解
配置一些加锁必须的参数说明
/*** 分布式锁注解*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {/*** 锁的场景** @return*/public String scene();/*** 加锁的key,优先取key(),如果没有,则取keyExpression()** @return*/public String key() default DistributeLockConstant.NONE_KEY;/*** SPEL表达式:* <pre>* #id* #insertResult.id* </pre>** @return*/public String keyExpression() default DistributeLockConstant.NONE_KEY;/*** 超时时间,毫秒* 默认情况下不设置超时时间,会自动续期** @return*/public int expireTime() default DistributeLockConstant.DEFAULT_EXPIRE_TIME;/*** 加锁等待时长,毫秒* 默认情况下不设置等待时长,会一直等待直到获取到锁* @return*/public int waitTime() default DistributeLockConstant.DEFAULT_WAIT_TIME;
}
实现分布式锁切面
加锁逻辑
- 反射的方式获取方法体 并从方法体上拿到我们定义的注解和注解里面配置的值
- 判断是否设置了key 如果没有 判断是否设置了SqEL表达式 如果都没有 抛出异常
- 如果是设置了 SqEL 解析SqEL 拼接分布式锁key 如果设置了key 直接拼接分布式锁key
- 如果没有设置等待时间 通过lock()加锁 默认无限等待 如果设置了等待实现 使用tryLock()配置等待时间
- 如果设置了超时时间 加锁时添加超时时间 默认单位是毫秒
- 执行方法体
- 判断锁是否被等钱线程占用 如果是则释放锁 避免将别的线程的锁给释放
/*** 分布式锁切面** @author hollis*/
@Aspect
@Component
@Order(Integer.MIN_VALUE + 1)
public class DistributeLockAspect {private RedissonClient redissonClient;public DistributeLockAspect(RedissonClient redissonClient) {this.redissonClient = redissonClient;}private static final Logger LOG = LoggerFactory.getLogger(DistributeLockAspect.class);@Around("@annotation(cn.hollis.nft.turbo.lock.DistributeLock)")public Object process(ProceedingJoinPoint pjp) throws Exception {Object response = null;Method method = ((MethodSignature) pjp.getSignature()).getMethod();DistributeLock distributeLock = method.getAnnotation(DistributeLock.class);String key = distributeLock.key();if (DistributeLockConstant.NONE_KEY.equals(key)) {if (DistributeLockConstant.NONE_KEY.equals(distributeLock.keyExpression())) {throw new DistributeLockException("no lock key found...");}/*** 例如* @DistributeLock(* scene = "order",* keyExpression = "#orderId"* )* public void processOrder(String orderId, User user) {* // 处理订单逻辑* }**///拿到解析器SpelExpressionParser parser = new SpelExpressionParser();//解析SqELExpression expression = parser.parseExpression(distributeLock.keyExpression());EvaluationContext context = new StandardEvaluationContext();/*** processOrder("ORD123", user)*/// 获取参数值 ["ORD123", user]Object[] args = pjp.getArgs();// 获取运行时参数的名称// (String orderId, User user)StandardReflectionParameterNameDiscoverer discoverer= new StandardReflectionParameterNameDiscoverer();String[] parameterNames = discoverer.getParameterNames(method);// 将参数绑定到context中/*** context.setVariable("orderId", "ORD123");* context.setVariable("user", user);*/if (parameterNames != null) {for (int i = 0; i < parameterNames.length; i++) {context.setVariable(parameterNames[i], args[i]);}}//expression.getValue(context) 解析 #orderId 表达式,获取绑定的参数值 "ORD123"。key = String.valueOf(expression.getValue(context));}String scene = distributeLock.scene();//结合 scene = "order",最终锁键为:order#ORD123String lockKey = scene + "#" + key;int expireTime = distributeLock.expireTime();//配置过期时间int waitTime = distributeLock.waitTime();//等待获取锁的时间RLock rLock= redissonClient.getLock(lockKey);// 获取Redisson实例try {boolean lockResult = false;//根据不同的锁等待时间 选择不同的枷锁策略//如果是默认 无限等待时间 适用于非高并发场景if (waitTime == DistributeLockConstant.DEFAULT_WAIT_TIME) {//没有设置超时时间if (expireTime == DistributeLockConstant.DEFAULT_EXPIRE_TIME) {LOG.info(String.format("lock for key : %s", lockKey));rLock.lock();} else {//设置了超时时间 默认单位是毫秒LOG.info(String.format("lock for key : %s , expire : %s", lockKey, expireTime));rLock.lock(expireTime, TimeUnit.MILLISECONDS);}lockResult = true;} else {//带超时时间的锁 适用于高并发场景//未设置锁的超时时间if (expireTime == DistributeLockConstant.DEFAULT_EXPIRE_TIME) {LOG.info(String.format("try lock for key : %s , wait : %s", lockKey, waitTime));lockResult = rLock.tryLock(waitTime, TimeUnit.MILLISECONDS);} else {//设置了锁的超时时间LOG.info(String.format("try lock for key : %s , expire : %s , wait : %s", lockKey, expireTime, waitTime));lockResult = rLock.tryLock(waitTime, expireTime, TimeUnit.MILLISECONDS);}}//获取锁失败 抛出异常if (!lockResult) {LOG.warn(String.format("lock failed for key : %s , expire : %s", lockKey, expireTime));throw new DistributeLockException("acquire lock failed... key : " + lockKey);}LOG.info(String.format("lock success for key : %s , expire : %s", lockKey, expireTime));//执行目标方法response = pjp.proceed();} catch (Throwable e) {throw new Exception(e);} finally {//最后如果锁被当前线程占有 释放当前线程的锁 避免误释放if (rLock.isHeldByCurrentThread()) {rLock.unlock();LOG.info(String.format("unlock for key : %s , expire : %s", lockKey, expireTime));}}return response;}
}