分布式锁注解@RedisLock,接口幂等性

第一步,切片类使用RedisDistributedLockAspect监听@RedisLock主解

RedisConfig配置类

@Configuration
public class RedisConfig {
    @Bean
    public RedisDistributedLockAspect createRedisDistributedLockAspect(ObjectProvider<RedisTemplate> redisTemplate){
        RedisDistributedLockAspect aspect = new RedisDistributedLockAspect();
        aspect.setRedisTemplate(redisTemplate.getIfAvailable());
        return aspect;
    }
}

RedisLock源码类

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisLock {

    /** 缓存Key,支持SPEL表达式 **/
    String value() default "";
 
    String tips() default "重复请求";
 
    /** 过期时间(毫秒) **/
    long expire() default 3000L;
 
    /** 最大过期时间(毫秒) **/
    long maxExpire() default 300000L;
 
    /** 方法执行完释放 **/
    boolean quitRelease() default true;
}

RedisDistributedLockAspect 的源码,最关键了,逻辑都在这里面

@Aspect
public class RedisDistributedLockAspect {
    public static final String KEY_PREFIX = "DUPLICATION_SUBMIT:";
    private RedisTemplate redisTemplate;

    public RedisDistributedLockAspect() {
    }

    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Pointcut("@annotation(com.dstcar.common.utils.lock.RedisLock)")
    public void getPointcut() {
    }

    @Around("getPointcut()")
    public Object doBefore(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature)point.getSignature();
        Method method = signature.getMethod();
        Object result;
        if (null != method && method.isAnnotationPresent(RedisLock.class)) {
            RedisLock redisLock = (RedisLock)method.getAnnotation(RedisLock.class);
            String lockKey = "DUPLICATION_SUBMIT:" + SpELAspectSupport.getKeyValue(point, redisLock.value());
            String requestId = UUID.randomUUID().toString();
            long expireTime = redisLock.expire();
            if (redisLock.quitRelease()) {
                expireTime = redisLock.maxExpire();
            }

            boolean res = this.tryGetDistributedLock(lockKey, requestId, expireTime);
            if (!res) {
                throw new DistributedLockException(redisLock.tips());
            }

            try {
                result = point.proceed();
            } finally {
                if (redisLock.quitRelease()) {
                    this.releaseDistributedLock(lockKey);
                }

            }
        } else {
            result = point.proceed();
        }

        return result;
    }

    public boolean tryGetDistributedLock(String lockKey, String requestId, long expireTime) {
        return this.redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.MILLISECONDS);
    }

    public boolean releaseDistributedLock(String lockKey) {
        return this.redisTemplate.delete(lockKey);
    }
}

第二步,接口层注解的使用

@PostMapping("/addInstallmentOrder")
    @RedisLock(value = "'addInstallmentOrder_'+#addInstallmentOrderDTO.sysUserId", tips = "请勿重复提交", quitRelease = false)
    public Results addInstallmentOrder(@RequestBody @Validated AddInstallmentOrderDTO addInstallmentOrderDTO) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("开始创建金融单");
        String financeLoanOrder= financeLoanOrderService.addInstallmentOrder(addInstallmentOrderDTO);
        //Thread.sleep(3000L);
        stopWatch.stop();
        log.info("结束创建金融单:{}s", stopWatch.getLastTaskTimeMillis()*0.001);
        return succeed(financeLoanOrder);
    }

针对源码,对分布式锁不起作用等函数的解析如下:

1、value值不填默认是"",可以在后面加上**#拼接请参的字段作为唯一的key值**

2、源码的tryGetDistributedLock()看作加锁lock()的操作,releaseDistributedLock()看作释放锁unlock()的操作

3、expire 过期时间 3000L指的是过期时间3s,maxExpire 300000L指的是最大过期时间300s,quitRelease() default true 默认为true的话,指的是方法执行完就释放锁了。

业务场景:

一、业务处理逻辑比较快的,1秒以内的,可以用StopWatch类判断出耗时时间。 记得设置quitRelease = false
因为:如果设置为true,1s以内执行完逻辑代码之后就释放锁了,
相同时间的请求又会请求进来不受限制了,这样看起来就会有重复的数据。当设置为false时,expire 起了作用,3s内即使逻辑代码走完也不会释放锁,等3s后才允许请他请求进来,这样同一个请求不会生成多条记录,保证了接口的幂等性。

二、业务处理逻辑比较慢的,可以设置quitRelease = true
因为:假如业务耗时是5秒,那么在这5秒内方法还没执行完,此时maxExpire最大过期数起了作用,自动过期时间不再等于expire3s而是等于maxExpire300s,所以3秒后锁也不会自动释放,要等业务方法走完才会释放锁。即使方法走完也没释放锁,那么锁也会在设置的maxExpire时间后自动释放的,看源码得出的结论。

重复测试接口工具:Fiddler

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值