写一个接口注解,里面内容是过期时间
@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("重复性操作");
}