引言
在处理高并发的Web应用中,一种常见的问题就是如何保证数据的一致性。为了解决这个问题,我们通常会使用锁。然而,当我们的应用部署在多台服务器上时,单机锁就无法满足我们的需求了,我们需要一个分布式锁。本文将介绍如何使用Redisson实现一个基于注解的Redis分布式锁(解决幂等性问题)。
添加Redisson依赖
首先,我们需要在项目中添加Redisson的依赖。
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.6.5</version>
</dependency>
创建锁注解
接下来,我们需要创建一个用于加锁的注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HasRedissonLock {
/**
* 锁的Key
*/
String value();
/**
* 锁的过期时间
*/
int leaseTime() default 10;
}
实现注解处理器
然后,我们需要创建一个注解处理器,用于处理带有 @HasRedissonLock 注解的方法。
@Aspect
@Component
public class RedissonLock {
@Autowired
private RedissonClient redissonClient;
@Around("execution(public * *(..)) && @annotation(com.app.aop.has.HasRedissonLock)")
public Object interceptor(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取登录用户ID,这里假设你有相应的方法获取用户ID
Long loginUserId = TokenUtils.getRequestUserId();
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 获取注解
HasRedissonLock hasRedissonLock = method.getDeclaredAnnotation(HasRedissonLock.class);
// 锁的Key
String lockKey = hasRedissonLock.value() + ":" + loginUserId;
// 锁的过期时间
long leaseTime = hasRedissonLock.leaseTime();
// 获取分布式锁对象
RLock lock = redissonClient.getLock(lockKey);
// 尝试获取锁
boolean isLocked = false;
try {
isLocked = lock.tryLock(leaseTime, TimeUnit.SECONDS);
if (!isLocked) throw new BusinessException("请勿重复请求");
// 执行目标方法
return joinPoint.proceed();
} finally {
// 释放锁
if (isLocked) lock.unlock();
}
}
}
在方法上使用注解
最后,我们可以在需要加锁的方法上使用 @HasRedissonLock 注解。
@HasRedissonLock("save_user")
@ApiOperation("添加用户")
@PostMapping()
public ApiResult<?> add(@RequestBody User user) {
user.setStatus(0);
user.setPassword(userService.encodePassword(user.getPassword()));
if (userService.saveUser(user)) {
return success("添加成功");
}
return fail("添加失败");
}
总结
通过以上步骤,我们实现了一个基于注解的分布式锁。这种方法的优点是,我们不需要在每个需要加锁的方法中添加重复的锁管理代码。