Implemente bloqueos distribuidos utilizando anotaciones personalizadas basadas en Redisson

Implemente bloqueos distribuidos utilizando anotaciones personalizadas basadas en Redisson

1. Principio de implementación

  1. Basado en Redisson;

  2. Implementada una anotación, el código es el siguiente:

    @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. Implementado un aspecto. Cuando un método usa la anotación @DistributedLock, adquirirá el bloqueo distribuido antes de ejecutar el método. El código se muestra a continuación:

    @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. En esencia, es lock.tryLock(distributedLock.acquireTimeoutMillis(),distributedLock.expireMillis(),TimeUnit.MILLISECONDS);un candado distribuido adquirido mediante;

  5. Debido a distributedLock.acquireTimeoutMillis()que no lo es 0, si un hilo no obtiene el bloqueo, continuará esperando el tiempo especificado y continuará intentando adquirir el bloqueo durante el período; el valor predeterminado actual es ese, ya que generalmente no toma distributedLock.acquireTimeoutMillis()tanto 10s. Si lleva mucho tiempo ejecutar la lógica empresarial, un hilo normalmente no esperará mucho tiempo 10s, que se utiliza principalmente para el resultado final;
    el tiempo de vencimiento del bloqueo es distributedLock.expireMillis()actualmente el predeterminado 8s, porque generalmente no lleva tanto tiempo ejecutar el negocio. lógica, por lo que en circunstancias normales un hilo no retendrá el bloqueo durante mucho tiempo 8s, principalmente para el resultado final;
    se utiliza para SPELobtener lockName;

2. Cómo utilizar

  1. En un método de cualquier capa, agregue @DistributedLock(name = "XXX")即可对"XXX"un candado;

  2. Ejemplo 1:

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

    Si el parámetro de entrada del método anterior codeses 111,222,333,444, entonces SPELel analizado lockNamees get111,222,333,444;

  3. Ejemplo 2:

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

    Si el parámetro de entrada del método anterior attendanceInfoes orderId, aaaes date, 2023-08-01entonces el SPELanalizado lockNamees attendance_infoaaa2023-08-01;

3. Precauciones

Cuando se utiliza, namese debe especificar name = "'<方法名/唯一标识名>'+#<入参/入参的某个字段>"para evitar diferentes métodos para generar lo mismo lockName;

Supongo que te gusta

Origin blog.csdn.net/itigoitie/article/details/132047955
Recomendado
Clasificación