0
点赞
收藏
分享

微信扫一扫

java实现分布式项目防止重复请求

写一个接口注解,里面内容是过期时间

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface NoRepeatSubmit {
    /**
     * 过期时长(毫秒)
     *
     * @return
     */
    long expire() default 5000;
}

写一个类  redis锁的类

@Slf4j
public class RedisLock {
    /**
     * 默认等待时长 1 毫秒
     */
    private static final long DEF_WAIT_TIME = 1L;

    /**
     * 默认过期时长 10 秒
     */
    private static final long DEF_EXPIRE_TIME = 10000 * 10L;

    /**
     * 默认重试次数
     */
    private static final Integer NO_TRY_COUNT = 0;
    /**
     * 默认重试休眠时长
     */
    private static final Long NO_TRY_SLEEP_TIME = 0L;

    private RedisLock() {
    }

    /**
     * 获取分布式锁
     *
     * @param redissonClient
     * @param lockKey        锁标识
     * @return
     */
    public static RLock getLock(RedissonClient redissonClient, String lockKey) {
        return redissonClient.getLock(lockKey);
    }

    /**
     * 尝试加锁
     *
     * @param lock 锁
     * @return
     */
    public static boolean lock(RLock lock) {
        return lock(lock, null);
    }

    /**
     * 尝试加锁
     *
     * @param lock       锁
     * @param conditions 附加条件
     * @return
     */
    public static boolean lock(RLock lock, Supplier<Boolean> conditions) {
        return lock(lock, NO_TRY_COUNT, NO_TRY_SLEEP_TIME, conditions);
    }

    /**
     * 尝试加锁
     *
     * @param lock       锁
     * @param tryCount   重试次数
     * @param sleepTime  重试休眠时长(毫秒)
     * @param conditions 附加条件
     * @return
     */
    public static boolean lock(RLock lock, int tryCount, long sleepTime, Supplier<Boolean> conditions) {
        return lock(lock, DEF_WAIT_TIME, DEF_EXPIRE_TIME, tryCount, sleepTime, conditions);
    }

    /**
     * 尝试加锁
     *
     * @param lock       锁
     * @param waitTime   获取锁等待时长(毫秒)
     * @param expireTime 获取锁后自动过期时长(毫秒)
     * @param tryCount   重试次数
     * @param sleepTime  重试休眠时长(毫秒)
     * @param conditions 附加条件
     * @return
     */
    public static boolean lock(RLock lock, long waitTime, long expireTime, int tryCount,
                               long sleepTime, Supplier<Boolean> conditions) {
        boolean result = false;
        try {
            boolean unlock = lock.tryLock(waitTime, expireTime, TimeUnit.MILLISECONDS);
            log.info("======> " + Thread.currentThread() + "尝试{}获取锁{} -> {}", tryCount, lock.getName(), unlock);
            if (unlock) {
                if (null != conditions) {
                    result = conditions.get();
                } else {
                    result = true;
                }
            } else {
                while (tryCount > 0) {
                    Thread.sleep(sleepTime);
                    --tryCount;
                    boolean res = lock(lock, waitTime, expireTime, tryCount, sleepTime, conditions);
                    if (res) {
                        result = true;
                        break;
                    }
                }
            }
        } catch (InterruptedException e) {
            log.error("RedisLock 获取分布式锁异常 -> {}", e.getMessage());
            Thread.currentThread().interrupt();
        }
        return result;
    }

    /**
     * 释放锁
     *
     * @param lock
     */
    public static void unlock(RLock lock) {
        if (null != lock) {
            log.info("<====== " + Thread.currentThread() + "释放锁{}", lock.getName());
            lock.unlock();
        }
    }

最后写一个横切的aop方法

@Aspect
@Component
@Slf4j
public class NoRepeatSubmitAop {
    private static final String TOKEN_KEY = "NoRepeatSubmit:";

    @Autowired
    private RedisService redisService;

    @Autowired
    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.huaze.ucap.office.redislock.NoRepeatSubmit)")
    public void serviceNoRepeat() {
        // 这是一个标记方法
    }

    @Around("serviceNoRepeat()")
    public R around(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String token = SecurityUtils.getUserCode();
        String url = request.getRequestURL().toString();
        String params = Arrays.toString(pjp.getArgs());
        String key = token + "-" + url + "-" + params;
        // 这里使用分布锁保证其线程安全(这里很关键)
        String lockKey = TOKEN_KEY + key;
        log.info("NoRepeatSubmit---key==,{}", lockKey);
        RLock lock = RedisLock.getLock(redissonClient, lockKey);
        boolean res = RedisLock.lock(lock);
        if (!res) {
            return R.fail("重复性操作");
        }
        Boolean boo = redisService.redisTemplate.hasKey(key);
        log.info("around===={}", boo);
        if (!boo) {
            log.info("NoRepeatSubmit==进入环绕通知");
            Object o = null;
            try {
                o = pjp.proceed();
                MethodSignature signature = (MethodSignature) pjp.getSignature();
                NoRepeatSubmit noRepeatSubmit = signature.getMethod().getAnnotation(NoRepeatSubmit.class);
                // 默认500毫秒内同一认证用户同一个地址同一个参数,视为重复提交
                redisService.setCacheObject(key, "1", noRepeatSubmit.expire(), TimeUnit.MILLISECONDS);
            } catch (ServiceException e) {
                log.error("around-ServiceException-异常,{}", e.getMessage());
                throw e;
            } catch (Exception e) {
                log.info("around-Exception-异常,{}", e.getMessage());
            } finally {
                RedisLock.unlock(lock);
            }
            JSONObject js = JSONObject.parseObject(JSONObject.toJSONString(o));
            log.info("加锁方法返回参数==={}",JSONObject.toJSONString(o));
            if ("200".equals(js.getString("code"))) {
                return R.ok(js.getString("data"));
            }else{
                return R.fail(js.getString("msg"));
            }
        } else {
            RedisLock.unlock(lock);
        }
        return R.fail("重复性操作");
    }

举报

相关推荐

0 条评论