redis实现分布式锁

1.用于后端重复提交和重复查询控制,控制幂等性;
2.使用redis键值set机制
3.可用于分布式场景
实现步骤:
1.增加自定义注解@RepeatSubmit
/**
 * 防止重复提交
 *
 * @author dyxu4
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface RepeatSubmit {
    /**
     * 锁定时间,根据接口情况给定锁时间,单位毫秒
     *
     * @return 锁定时间
     */
    long lockTime() default 1000;

    /**
     * 是否查询操作,默认否
     *
     * @return true/false 是否查询操作
     */
    boolean isSelect() default false;

}
2.增加切面实现
/**
 * 切面-防止重复提交
 *
 * @author dyxu4
 */
@Aspect
@Slf4j
@Component
public class RepeatSubmitAspect {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    /**
     * 加锁的key值开头
     */
    private static final String LOCK_HEADER = "lock:";

    /**
     * 切点
     *
     * @param repeatSubmit 注解参数
     */
    @Pointcut("@annotation(repeatSubmit)")
    public void pointCut(RepeatSubmit repeatSubmit) {
    }

    /**
     * 利用环绕通知进行处理重复提交问题
     *
     * @param pjp          切点信息
     * @param repeatSubmit 注解信息
     */
    @Around(value = "pointCut(repeatSubmit)", argNames = "pjp,repeatSubmit")
    public Object around(ProceedingJoinPoint pjp, RepeatSubmit repeatSubmit) throws Throwable {
        //获取锁定的时间,单位毫秒
        long lockSeconds = repeatSubmit.lockTime();
        boolean isSelect = repeatSubmit.isSelect();
        //获得request对象
        HttpServletRequest request = httpServletRequest();
        try {
            Assert.notNull(request, "request can not null");
        } catch (Exception e) {
            throw new BusinessException("请求异常,请重试");
        }
        // 此处可以用token或者JSessionId
        String token = request.getHeader("token");
        String path = request.getServletPath();
        String key = getKey(token, path);
        String clientId = getClientId();
        //锁定多少秒
        boolean isSuccess = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, clientId, lockSeconds, TimeUnit.MILLISECONDS));
        if (isSuccess) {
            log.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);
            // 获取锁成功, 执行进程
            Object result;
            try {
                result = pjp.proceed();
            } finally {
                //解锁
                redisTemplate.delete(key);
                log.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);
            }
            return result;
        } else {
            // 获取锁失败,认为是重复提交的请求
            log.info("tryLock fail, key = [{}]", key);
            String returnMsg = "重复请求,请稍后再试!";
            if (isSelect) {
                returnMsg = "请勿频繁刷新,稍后再试!";
            }
            throw new BusinessException(returnMsg);
        }
    }

    /**
     * 获得request对象
     *
     * @return 客户端请求request
     */
    private HttpServletRequest httpServletRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return Objects.requireNonNull(requestAttributes).getRequest();
    }

    /**
     * 获得请求key
     *
     * @param token 用户token
     * @param path  请求路径
     * @return key
     */
    private String getKey(String token, String path) {
        return LOCK_HEADER + token + path;
    }

    /**
     * 获得uuid
     *
     * @return 随机uuid
     */
    private String getClientId() {
        return UUID.randomUUID().toString();
    }

}

3.将注解添加到想要控制的接口(controller)

  /**
     * 系统任务分页列表
     *
     * @return PageVue<TaskListVo>
     */
    @PostMapping("/page")
    @RepeatSubmit(lockTime = 2000,isSelect = true)
    @OperationLog(name = "系统任务分页列表", type = OperationLogType.LIST)
    @ApiOperation(value = "系统任务分页列表", response = TaskPageListVo.class)
    public ApiResult<PageVue<TaskPageListVo>> getTaskPage(@Validated @RequestBody TaskPageParam taskPageParam) {
        LoginUserVo loginUserVo = LoginUtil.getLoginUserVo();
        PageVue<TaskPageListVo> pageVue = taskApplicationService.getTaskPage(loginUserVo, taskPageParam);
        return ApiResult.ok(pageVue);
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值