Redisson に基づくカスタム アノテーションを使用して分散ロックを実装する

Redisson に基づくカスタム アノテーションを使用して分散ロックを実装する

1. 実施原則

  1. レディソンをベースにしています。

  2. アノテーションを実装したコードは次のとおりです。

    @Target({
          
          ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DistributedLock {
          
          
    
        long TEN_THOUSAND = 10000L;
    
        long EIGHT_THOUSAND = 8000L;
    
        /**
         * 锁的名称
         */
        String name() default "";
    
        /**
         * 锁的过期时间,单位ms,默认8000
         */
        long expireMillis() default EIGHT_THOUSAND;
    
        /**
         * 获取锁的超时时间,单位ms,默认10000
         */
        long acquireTimeoutMillis() default TEN_THOUSAND;
    
    }
    
  3. アスペクトを実装しました。メソッドが @DistributedLock アノテーションを使用する場合、メソッドを実行する前に分散ロックを取得します。コードは以下のように表示されます:

    @Aspect
    @Component
    @Slf4j
    public class DistributedLockAspect {
          
          
    
        @Resource
        private RedissonClient redissonClient;
    
        @Pointcut("@annotation(com.xxx.xxx.xxx.xxx.DistributedLock)")
        public void myPointcut() {
          
          
    
        }
    
        @Around(value = "myPointcut()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
          
          
            DistributedLock distributedLock = getAnnotation(pjp);
            String lockName = resolveKey(distributedLock.name(), pjp);
            RLock lock = redissonClient.getLock(lockName);
            try {
          
          
    
                Stopwatch stopwatch = Stopwatch.createStarted();
                boolean success = lock.tryLock(distributedLock.acquireTimeoutMillis(), distributedLock.expireMillis(),
                    TimeUnit.MILLISECONDS);
                LOGGER.info("tryLock, result:{}, cost:{}", success, stopwatch.stop().elapsed(TimeUnit.MILLISECONDS));
                if (success) {
          
          
                    LOGGER.info("获取锁成功,lockName={}", lockName);
                } else {
          
          
                    LOGGER.info("获取锁失败,lockName={}", lockName);
                }
                return pjp.proceed();
            } finally {
          
          
                if (lock.isHeldByCurrentThread()) {
          
          
                    lock.unlock();
                    LOGGER.info("释放锁成功,lockName={}", lockName);
                }
            }
        }
    
        private DistributedLock getAnnotation(ProceedingJoinPoint pjp) {
          
          
            MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
            return methodSignature.getMethod().getAnnotation(DistributedLock.class);
        }
    
        private String resolveKey(String key, ProceedingJoinPoint pjp) {
          
          
            if (StringUtils.isBlank(key)) {
          
          
                return pjp.getSignature().toLongString();
            } else {
          
          
                StandardEvaluationContext context = new StandardEvaluationContext();
                Object[] args = pjp.getArgs();
                MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
                String[] params = methodSignature.getParameterNames();
                for (int i = 0; i < params.length; i++) {
          
          
                    context.setVariable(params[i], args[i]);
                }
                return new SpelExpressionParser().parseExpression(key).getValue(context, String.class);
            }
        }
    }
    
  4. 本質的に、これはlock.tryLock(distributedLock.acquireTimeoutMillis(),distributedLock.expireMillis(),TimeUnit.MILLISECONDS);次の方法で取得された分散ロックです。

  5. distributedLock.acquireTimeoutMillis()そうでないため0、スレッドがロックを取得できない場合、スレッドは指定された時間待機し続け、その期間中はロックの取得を試行し続けます。通常はロックの取得に時間がかからないため、現在のデフォルトは次のとおりですdistributedLock.acquireTimeoutMillis()10sビジネス ロジックの実行に時間がかかるため、スレッドは通常は待機しません。 長時間待機します。10sこれは主に最終的な目的で使用されます。通常、ビジネスの実行にはそれほど時間がかからないため、
    ロックの有効期限はdistributedLock.expireMillis()現在デフォルトです。8sこのロジックのため、通常の状況では、スレッドは主に最終的な目的のために長時間ロックを保持しません8s取得する
    ために使用しますSPELlockName

2. 使用方法

  1. 任意のレイヤーのメソッドにロックを追加します@DistributedLock(name = "XXX")即可对"XXX"

  2. 例 1:

    @DistributedLock(name = "'get'+#codes")
    public void get(String codes) {
          
          
        List<String> codeList = Arrays.stream(orgCodes.split(",")).collect(Collectors.toList());
        ext(codeList);
    }
    

    上記のメソッドの入力パラメータがcodesの場合111,222,333,444SPEL解析されたパラメータlockNameget111,222,333,444;

  3. 例 2:

    @DistributedLock(name = "'attendance_info'+#attendanceInfo.orderId+#attendanceInfo.date")
    public Integer insertDedup(ConstructionAttendanceInfo attendanceInfo, AtomicInteger dmlType) {
          
          
    	// 此处省略
    	return 0;
    }
    

    上記のメソッドの入力パラメータがattendanceInfo, orderIdisaaa場合date解析されたパラメータは;です。2023-08-01SPELlockNameattendance_infoaaa2023-08-01

3. 注意事項

使用する場合は、異なるメソッドが同じ を生成することを避けるためにname指定する必要がありますname = "'<方法名/唯一标识名>'+#<入参/入参的某个字段>"lockName

おすすめ

転載: blog.csdn.net/itigoitie/article/details/132047955