公司做分布式的订单业务,需要增加分布式锁进行竞争资源,需要增加锁降级功能
简单实现了一下,支持 SPEL ,有更好的建议尽情提出
注解代码:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheLock {
/**
* 支持 SPEL 表达式
*/
String key();
long expTime() default 10;
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
AOP 切面:
@Slf4j
@Aspect
@Component
public class CacheLockAop {
/**
* 最大等待时间 ms
*/
private static final long MAX_WAIT_TIME = 30000;
@Resource
private Redisson orderRedisson;
@Around("@within(cacheLock) || @annotation(cacheLock)")
public Object cacheLock(ProceedingJoinPoint jp, CacheLock cacheLock) throws Throwable {
Object result = Response.fail(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMsg());
boolean locked = false;
try {
String lockKey = CacheKeyConstant.ORDER_LOCK_KEY + this.getLockKey(jp, cacheLock.key());
log.info("CacheLockAop#cacheLock 获取 key : {}", lockKey);
RLock rLock = orderRedissonExt.getLock(lockKey);
locked = rLock.tryLock(cacheLock.timeUnit().convert(MAX_WAIT_TIME, TimeUnit.MILLISECONDS), cacheLock.expTime(), cacheLock.timeUnit());
if (locked) {
try {
result = jp.proceed();
} finally {
rLock.unlock();
}
} else {
log.error("CacheLockAop#cacheLock 外汇跟单gendan5.com 缓存锁获取失败, key : {}", lockKey);
result = Response.fail(CodeEnum.ERROR.getCode(), " 缓存锁获取失败,请稍后重试 ");
}
} catch (Throwable e) {
log.error("CacheLockAop#cacheLock ERROR", e);
// 异常后没有获取锁执行业务代码
if (!locked) {
log.warn(" 缓存锁添加失败,降级执行 ...");
result = jp.proceed();
}
}
return result;
}
private String getLockKey(ProceedingJoinPoint jp, String elStr) throws NoSuchMethodException {
LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
EvaluationContext context = new StandardEvaluationContext();
String[] paraNameArr = discoverer.getParameterNames(this.getTargetMethod(jp));
Object[] args = jp.getArgs();
for (int i = 0; i < paraNameArr.length; i++) {
context.setVariable(paraNameArr[i], args[i]);
}
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression(elStr);
String lockKey = expression.getValue(context, String.class);
if (StringUtils.isBlank(lockKey)) {
throw new RuntimeException("CacheLockAop#getLockKey 获取 lockKey 为空! ");
}
return lockKey;
}
private Method getTargetMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException {
Signature signature = pjp.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method agentMethod = methodSignature.getMethod();
return pjp.getTarget().getClass().getMethod(agentMethod.getName(), agentMethod.getParameterTypes());
}
}
使用方法:
@Override
@CacheLock(key = "#orderReq.srcSystem + '_' + #orderReq.orderId")
public Response
// 你的业务代码
}