基于Redisson实现对分布式锁的注解式封装
实现结果
可以基于注解的方式给一个方法体添加分布式锁 并实现自动释放
示例
java
@Service
public class OrderService {
@DistributeLock(
scene = "order",
keyExpression = "#orderId",
expireTime = 3000,
waitTime = 1000
)
public void createOrder(String orderId, User user) {
// 处理订单创建逻辑,自动加锁/解锁
// 锁键为 "order#123"(假设 orderId=123)
}
@DistributeLock(
scene = "stock",
key = "product_#productId",
expireTime = 5000
)
public void deductStock(Long productId, int quantity) {
// 锁键为 "stock#product_1001"(假设 productId=1001)
}
}
实现@DistributeLock注解
配置一些加锁必须的参数说明
java
/**
* 分布式锁注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {
/**
* 锁的场景
*
* @return
*/
public String scene();
/**
* 加锁的key,优先取key(),如果没有,则取keyExpression()
*
* @return
*/
public String key() default DistributeLockConstant.NONE_KEY;
/**
* SPEL表达式:
* <pre>
* #id
* #insertResult.id
* </pre>
*
* @return
*/
public String keyExpression() default DistributeLockConstant.NONE_KEY;
/**
* 超时时间,毫秒
* 默认情况下不设置超时时间,会自动续期
*
* @return
*/
public int expireTime() default DistributeLockConstant.DEFAULT_EXPIRE_TIME;
/**
* 加锁等待时长,毫秒
* 默认情况下不设置等待时长,会一直等待直到获取到锁
* @return
*/
public int waitTime() default DistributeLockConstant.DEFAULT_WAIT_TIME;
}
实现分布式锁切面
加锁逻辑
- 反射的方式获取方法体 并从方法体上拿到我们定义的注解和注解里面配置的值
- 判断是否设置了key 如果没有 判断是否设置了SqEL表达式 如果都没有 抛出异常
- 如果是设置了 SqEL 解析SqEL 拼接分布式锁key 如果设置了key 直接拼接分布式锁key
- 如果没有设置等待时间 通过lock()加锁 默认无限等待 如果设置了等待实现 使用tryLock()配置等待时间
- 如果设置了超时时间 加锁时添加超时时间 默认单位是毫秒
- 执行方法体
- 判断锁是否被等钱线程占用 如果是则释放锁 避免将别的线程的锁给释放
java
/**
* 分布式锁切面
*
* @author hollis
*/
@Aspect
@Component
@Order(Integer.MIN_VALUE + 1)
public class DistributeLockAspect {
private RedissonClient redissonClient;
public DistributeLockAspect(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
private static final Logger LOG = LoggerFactory.getLogger(DistributeLockAspect.class);
@Around("@annotation(cn.hollis.nft.turbo.lock.DistributeLock)")
public Object process(ProceedingJoinPoint pjp) throws Exception {
Object response = null;
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
DistributeLock distributeLock = method.getAnnotation(DistributeLock.class);
String key = distributeLock.key();
if (DistributeLockConstant.NONE_KEY.equals(key)) {
if (DistributeLockConstant.NONE_KEY.equals(distributeLock.keyExpression())) {
throw new DistributeLockException("no lock key found...");
}
/**
* 例如
* @DistributeLock(
* scene = "order",
* keyExpression = "#orderId"
* )
* public void processOrder(String orderId, User user) {
* // 处理订单逻辑
* }
*
*/
//拿到解析器
SpelExpressionParser parser = new SpelExpressionParser();
//解析SqEL
Expression expression = parser.parseExpression(distributeLock.keyExpression());
EvaluationContext context = new StandardEvaluationContext();
/**
* processOrder("ORD123", user)
*/
// 获取参数值 ["ORD123", user]
Object[] args = pjp.getArgs();
// 获取运行时参数的名称
// (String orderId, User user)
StandardReflectionParameterNameDiscoverer discoverer
= new StandardReflectionParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
// 将参数绑定到context中
/**
* context.setVariable("orderId", "ORD123");
* context.setVariable("user", user);
*/
if (parameterNames != null) {
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
}
//expression.getValue(context) 解析 #orderId 表达式,获取绑定的参数值 "ORD123"。
key = String.valueOf(expression.getValue(context));
}
String scene = distributeLock.scene();
//结合 scene = "order",最终锁键为:order#ORD123
String lockKey = scene + "#" + key;
int expireTime = distributeLock.expireTime();//配置过期时间
int waitTime = distributeLock.waitTime();//等待获取锁的时间
RLock rLock= redissonClient.getLock(lockKey);// 获取Redisson实例
try {
boolean lockResult = false;
//根据不同的锁等待时间 选择不同的枷锁策略
//如果是默认 无限等待时间 适用于非高并发场景
if (waitTime == DistributeLockConstant.DEFAULT_WAIT_TIME) {
//没有设置超时时间
if (expireTime == DistributeLockConstant.DEFAULT_EXPIRE_TIME) {
LOG.info(String.format("lock for key : %s", lockKey));
rLock.lock();
} else {
//设置了超时时间 默认单位是毫秒
LOG.info(String.format("lock for key : %s , expire : %s", lockKey, expireTime));
rLock.lock(expireTime, TimeUnit.MILLISECONDS);
}
lockResult = true;
} else {
//带超时时间的锁 适用于高并发场景
//未设置锁的超时时间
if (expireTime == DistributeLockConstant.DEFAULT_EXPIRE_TIME) {
LOG.info(String.format("try lock for key : %s , wait : %s", lockKey, waitTime));
lockResult = rLock.tryLock(waitTime, TimeUnit.MILLISECONDS);
} else {
//设置了锁的超时时间
LOG.info(String.format("try lock for key : %s , expire : %s , wait : %s", lockKey, expireTime, waitTime));
lockResult = rLock.tryLock(waitTime, expireTime, TimeUnit.MILLISECONDS);
}
}
//获取锁失败 抛出异常
if (!lockResult) {
LOG.warn(String.format("lock failed for key : %s , expire : %s", lockKey, expireTime));
throw new DistributeLockException("acquire lock failed... key : " + lockKey);
}
LOG.info(String.format("lock success for key : %s , expire : %s", lockKey, expireTime));
//执行目标方法
response = pjp.proceed();
} catch (Throwable e) {
throw new Exception(e);
} finally {
//最后如果锁被当前线程占有 释放当前线程的锁 避免误释放
if (rLock.isHeldByCurrentThread()) {
rLock.unlock();
LOG.info(String.format("unlock for key : %s , expire : %s", lockKey, expireTime));
}
}
return response;
}
}