Redisson分布式锁实战+AOP
前言
在实现分布式锁得基础上,利用自定义注解+AOP实现参数级别上的分布式锁。只需要在方法上加上自定义的注解即可,还可选择锁定参数,比较方便
自定义注解@DistributeLock
/**
* 分布式锁注解,用于方法级别的分布式控制
*
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {
/**
* 要锁哪几个位置的参数,默认不锁参数
*/
int[] lockIndex() default {-1};
/**
* 等待时间,默认2000毫秒
*/
int waitTime() default 2000;
/**
* 自动失效时间,默认30000毫秒
*/
int expireTime() default 30000;
/**
* 时间类型,默认毫秒
*/
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}
对加上了@DistributeLock注解的方法进行@Around增强
/**
* 方法级别的分布式锁
*
* @author : itoyoung
*/
@Component
@Aspect
@Slf4j
public class DistributeLockAspect {
@Resource
private RedissonLock redissonLock;
@Around("@annotation(lock)")
public Object syncLock(ProceedingJoinPoint joinPoint, DistributeLock lock) {
Signature signature = joinPoint.getSignature();
StringBuilder lockKeyBuilder = new StringBuilder(signature.getDeclaringTypeName() + "." + signature.getName());
Object[] args = joinPoint.getArgs();
int[] lockIndex = lock.lockIndex();
if (lockIndex.length > 0 && lockIndex[0] >= 0) {
for (int index : lockIndex) {
if (index >= args.length || index < 0) {
throw new BaseRuntimeException("参数索引lockIndex: " + index + " 异常");
}
lockKeyBuilder.append(".").append(JSON.toJSONString(args[index]));
}
}
// 防止key值太长,用根据其生成的hash值做key
String lockKey = StringUtil.md5(lockKeyBuilder.toString());
boolean res = redissonLock.tryLock(lockKey, lock.expireTime(), lock.waitTime(), lock.timeUnit());
if (res) {
log.info("AspectLock: key: {} 加锁成功", lockKeyBuilder);
try {
return joinPoint.proceed();
} catch (Throwable e) {
log.error("AspectLock: key: {} 业务异常", lockKeyBuilder);
throw new BaseRuntimeException(e.getMessage());
} finally {
// 释放锁
redissonLock.unLock(lockKey);
log.info("AspectLock: key: {} 释放锁成功", lockKeyBuilder);
}
} else {
log.warn("AspectLock: key: {} 获取锁失败", lockKeyBuilder);
throw new BaseRuntimeException("异常错误,请稍后重试");
}
}
}
实测
@RestController
@Slf4j
@RequestMapping("/test/redissonLock")
public class RedissonLockTestController {
@Resource
private ILockCountService lockCountService;
@Resource
private ILockStatusService lockStatusService;
/**
* 对方法加上分布式锁
*
* @param lockName
* @param users
* @return
*/
@PostMapping("/aspectLock/{lockName}")
@DistributeLock(lockIndex = {0, 1}, waitTime = 3, expireTime = 30, timeUnit = TimeUnit.SECONDS)
public Result aspectLock(@PathVariable("lockName") String lockName, @RequestBody List<User> users) {
LockStatus status = lockStatusService.getById(1);
if (status.getStatus().equals("N")) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockCount count = lockCountService.getOne(new QueryWrapper<LockCount>().lambda().eq(LockCount::getLockId, status.getId()));
count.setCount(count.getCount() + 1);
lockCountService.updateById(count);
status.setStatus("Y");
lockStatusService.updateById(status);
}
return new Result().defaultSuccess();
}
}
实测五百线程并发访问,count只自增了一次,结果正确