基于redlock和自定义注解一行代码实现api分布式锁

384 阅读2分钟

最近接手一个很烂的项目代码,代码结构天马行空没任何规范[哭],

然后看看哪里可以改改,发现其中一个大量重复编写的分布式锁 逻辑 ,

用于在编辑或添加或执行定时任务时保证只有一个请求抢占资源,避免并发或重复问题。

目前采用引入redlock,用redis来实现分布式锁。

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId></dependency>

实现代码:

public void biz() {RLock lock = redissonClient.getLock(FINANCE_OUTER_ASSET_SCHEDULE_LOCK + LocalDate.now());try {if (lock.tryLock(3, 30, TimeUnit.SECONDS)) {....do something...lock.unlock();}} catch (InterruptedException e) {Thread.currentThread().interrupt();lock.unlock();} finally {if (lock.isLocked()) {lock.unlock();}}}

大概用法是 : 先尝试获取某个key的lock,获取到的就执行业务逻辑,获取不到根据lock的参数决定等待还是重试

该段代码在项目代码中大量重复且不易于维护和带来大量的重复编写工作量等很low的问题。

为减少工作量提高代码整洁干净精简,所以我采用注解的方式进行改造 :

首先需要定义一个注解,关键属性就是使用时可以自定义key值,等待时间,释放时间,重试次数等参数:

/** 重复执行锁定* @author lilinjun*/@Target({METHOD})@Retention(RUNTIME)@Documentedpublic @interface ReqLock {String message() default "{资源被锁定稍后再试结束}";RLockKeys key() default RLockKeys.DEFAULT;long waitTime() default 300;long leaseTime() default 1000;TimeUnit unit() default TimeUnit.MILLISECONDS;Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};@Target({METHOD})@Retention(RUNTIME)@Documented@interface List {ReqLock[] value();}}

然后写个aop

@Aspect@Slf4j@Servicepublic class ReqAspect {@Resourceprivate RedissonClient redissonClient;@Pointcut("@annotation(com.huobi.finance.voucher.common.annotation.ReqLock)")public void reqLockAop() {}@Before("reqLockAop()&&@annotation(reqLock)")public void before(JoinPoint joinPoint, ReqLock reqLock) {}@Around("reqLockAop()")public Object around(ProceedingJoinPoint pjp) throws Throwable {Method method = ((MethodSignature) pjp.getSignature()).getMethod();ReqLock req = method.getAnnotation(ReqLock.class);RLockKeys lockKeyEnum = req.key();String key = lockKeyEnum.getType();String value = lockKeyEnum.getValue();if (StringUtils.isNotEmpty(value)) {//如果你的rediskey值需要根据入参来实时拼装,在这里解析入参进行拼装Object[] args = pjp.getArgs();}RLock lock = redissonClient.getLock(key);if (!lock.tryLock(req.waitTime(), req.leaseTime(), req.unit())) {log.info("get lock failed : " + key);return null;}log.info("get lock success : " + key);try {return pjp.proceed();} catch (Exception e) {log.info("execute locked method occured an exception", e);} finally {lock.unlock();log.info("release lock :" + key);}return null;}}

使用示例, 这里用于放在controller的方法上:

@PostMapping("/update")@ReqLock(key = RLockKeys.CURRENCY)public Result update(@Validated @RequestBody CurrencyModifyReq req) {currencyService.update(req);return Result.success(null);}

后续需要用到分布式锁只需要一行代码就可以,避免大量入侵业务逻辑代码的做法。