Springboot+Aop註解的方式實現分散式鎖

專注的阿熊發表於2021-07-28

公司做分散式的訂單業務,需要增加分散式鎖進行競爭資源,需要增加鎖降級功能

簡單實現了一下,支援 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<Boolean> submitOrder(OrderReq orderReq) {

         // 你的業務程式碼

     }


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946337/viewspace-2783883/,如需轉載,請註明出處,否則將追究法律責任。

相關文章