Реализация распределенных блокировок с использованием пользовательских аннотаций на основе 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, в основном для чистой прибыли;
    используйте для SPELполучения lockName;

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,444, то SPELанализируемый параметр lockNameравен get111,222,333,444;

  3. Пример 2:

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

    Если входной параметр вышеуказанного метода attendanceInfoравен orderId, aaais date, 2023-08-01то SPELанализируемый параметр lockNameравен attendance_infoaaa2023-08-01;

3. Меры предосторожности

При использовании nameего следует указать name = "'<方法名/唯一标识名>'+#<入参/入参的某个字段>", чтобы избежать создания одного и того же lockName;

Guess you like

Origin blog.csdn.net/itigoitie/article/details/132047955