redis分布式锁(redisson)的简单封装

tech2023-05-23  117

项目用到了redisson分布式锁,但是每个地方的代码除了业务代码,其他都差不多一样的,如果要修改的话,就要修改很多,不只修改一个项目,其他项目也得该,地方多了容易出错,并且数据也对不上。起始的想法是封装起来,然后自定义一个spring boot starter。其他项目依赖即可,这样一来如果要修改只改这个starter模块就可以了。于是将项目分布式锁进行了简单封装,以前是下面代码这样的

@Autowired private RedissonClient redissonClient; public void syncHuifuBill() throws InterruptedException { RLock redLock = redissonClient.getLock(profiles+"_HUIFU_REDLOCK_KEY"); boolean b = redLock.tryLock(0,30000,TimeUnit.MILLISECONDS); try { //自己的业务逻辑 } catch (Exception e) { logger.error("交易对账单同步数据库失败", e); }finally { if(b){ if(redLock.isLocked()){ redLock.unlock(); } } } }

每个地方都是try catch finally这种类似的,代码看着也不舒服,后期维护动的代码也比较多。

解决办法

   下面代码只是简单写了下两种思路,实际项目中不止这点

  ①自定义注解

   需要在枷锁的方法上面贴一个注解,利用Spring AOP动态代理的特性进行处理,配置自己的参数值即可

  自定义注解

package com.example.ann; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author yupanpan * @Description: 锁注解 * @date 2020/9/3 11:22 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface DistributeLock { /** * @author 锁的key前缀 */ String lockKeyPrefix() default ""; /** * @author 业务code */ String businessCode() default ""; /** * @author 获取锁的等待时间(毫秒) */ int waitTime() default 0; /** * @author 释放的时间(毫秒) */ int leaseTime() default 0; }

  AOP

package com.example.aop; import com.example.ann.DistributeLock; import com.example.exception.DistributeLockException; 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.reflect.MethodSignature; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; /** * @author yupanpan * @Description: 分布式锁切面 * @date 2020/9/3 11:45 */ @Aspect @Component public class DistributeLockAspect { @Autowired private RedissonClient redissonClient; @Around("@annotation(com.example.ann.DistributeLock)") public Object interceptor(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); DistributeLock distributeLock = method.getAnnotation(DistributeLock.class); String lockKey=getLockKeyName(distributeLock,joinPoint); RLock rLock=redissonClient.getLock(lockKey); boolean b=getLock(rLock,distributeLock); try { if(b){ return joinPoint.proceed(); }else { throw new DistributeLockException("网络繁忙,请稍后再试"); } }catch (Exception e){ throw new DistributeLockException("服务器繁忙,请稍后再试"); }finally { if(b){ if(rLock.isLocked()){ rLock.unlock(); } } } } private String getLockKeyName(DistributeLock distributeLock,ProceedingJoinPoint joinPoint) { String lockKeyPrefix = distributeLock.lockKeyPrefix(); String businessCode = distributeLock.businessCode(); if(StringUtils.isNotBlank(lockKeyPrefix)&&StringUtils.isNotBlank(businessCode)){ return lockKeyPrefix+":"+businessCode; }else if(StringUtils.isNotBlank(lockKeyPrefix)){ return lockKeyPrefix; }else if(StringUtils.isNotBlank(businessCode)){ return businessCode; }else { String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); return className+"."+methodName; } } private boolean getLock(RLock rLock, DistributeLock distributeLock) throws InterruptedException { int waitTime = distributeLock.waitTime(); int leaseTime = distributeLock.leaseTime(); boolean b=false; if(waitTime>0&&leaseTime>0){ b=rLock.tryLock(waitTime,leaseTime, TimeUnit.MICROSECONDS); }else if(leaseTime>0){ b=rLock.tryLock(0,leaseTime,TimeUnit.MILLISECONDS);; }else if(waitTime>0){ b=rLock.tryLock(waitTime,TimeUnit.MILLISECONDS); }else { b=rLock.tryLock(); } return b; } }

测试使用

@DistributeLock(lockKeyPrefix = "MAIL", businessCode = "ABEODS", waitTime = 0, leaseTime = 30000) @GetMapping("/distribute/lock") public String distributeLock() throws Exception { return "分布式锁注解测试...."; }

 

 ②封装获取锁的过程

  直接调用工具类方法,只关心业务逻辑处理部分,可以拓展成多个接口实现类实现不同的业务锁

package com.example.service.lock; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; /** * @author yupanpan redisson分布式锁工具类 * @Description: a * @date 2020/9/3 11:14 */ @Service @Transactional public class RedisDistributeLock { @Autowired private RedissonClient redissonClient; public <T> T lock(String key, int waitTime, int leaseTime, Supplier<T> success,Supplier<T> fail) throws Exception { RLock lock = redissonClient.getLock(key); boolean b = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS); try { if(b){ return success.get(); }else { return fail.get(); } }finally { if(b){ if(lock.isLocked()){ lock.unlock(); } } } } }

函数式接口 Supplier<T>

T:出参类型;没有入参

调用方法:T get();

定义函数示例:Supplier<Integer> supplier= () -> 100;    // 常用于业务“有条件运行”时,符合条件再调用获取结果的应用场景;运行结果须提前定义,但不运行。

调用函数示例:supplier.get();

 

测试使用

@GetMapping("/lock") public String lock() throws Exception { return redisDistributeLock.lock("MAIL",0,30000,()->{ //获取锁成功执行逻辑 return "获取锁成功"; },()->{ //获取锁失败执行逻辑 return "获取锁失败"; }); }

 

区别

维护都比较方便。第一种适用于方法级别的,代码更优雅舒服一些。第二种通用,整个方法全部代码块和部分代码块都可,业务拓展性更高

 

最新回复(0)