springboot2.3 formulario de anotación personalizada de bloqueo distribuido integrado de redisson

Esto no introduce la integración de springboot ni la integración de la configuración de redis, solo los bloqueos distribuidos de redisson 

Confiar principalmente en

 <!--redis-->
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

 <!--分布式锁redisson-->
<dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson</artifactId>
      <version>3.5.0</version>
    </dependency>
 <!--aop-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

archivo de configuración de redission

application-single-dev.yml

Modo autónomo

singleServerConfig:
  idleConnectionTimeout: 10000 #连接空闲超时(毫秒),默认10000
  connectTimeout: 10000 #连接空闲超时(毫秒),默认10000
  timeout: 3000 #命令等待超时(毫秒),默认3000
  retryAttempts: 3 #命令失败重试次数
  retryInterval: 1500 #命令重试发送时间间隔(毫秒),默认1500
  password: null
  subscriptionsPerConnection: 5 #单个连接最大订阅数量,默认5
  clientName: null #客户端名称
  address: "redis://127.0.0.1:6379"
  subscriptionConnectionMinimumIdleSize: 1 #发布和订阅连接的最小空闲连接数,默认1
  subscriptionConnectionPoolSize: 50 #发布和订阅连接池大小,默认50
  connectionMinimumIdleSize: 24 #最小空闲连接数,默认32
  connectionPoolSize: 64 #连接池大小,默认64
  database: 0
  dnsMonitoringInterval: 5000 #DNS监测时间间隔(毫秒),默认5000
threads: 16
nettyThreads: 32
codec: !<org.redisson.codec.JsonJacksonCodec> {}
#"transportMode": "NIO"

Modo de clúster:

clusterServersConfig:
  idleConnectionTimeout: 10000
  pingTimeout: 1000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  reconnectionTimeout: 3000
  failedAttempts: 3
  password: null
  subscriptionsPerConnection: 5
  clientName: null
  loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}
  slaveSubscriptionConnectionMinimumIdleSize: 1
  slaveSubscriptionConnectionPoolSize: 50
  slaveConnectionMinimumIdleSize: 32
  slaveConnectionPoolSize: 64
  masterConnectionMinimumIdleSize: 32
  masterConnectionPoolSize: 64
  readMode: "SLAVE"
  nodeAddresses:
  - "redis://127.0.0.1:7004"
  - "redis://127.0.0.1:7001"
  - "redis://127.0.0.1:7000"
  scanInterval: 1000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
#"transportMode":"NIO"

 

Inyectar el archivo de configuración

El archivo de configuración leído aquí se lee de acuerdo con el entorno

RedissionConfig
package com.dzhjj.dzhjjapi.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.redisson.config.Config;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;

/**
 * @Auther: heng
 * @Date: 2020/12/3 13:56
 * @Description: redission分布式锁
 * @Version 1.0.0
 */
@Order(2)
@Slf4j
@Configuration
public class RedissionConfig {
  //@Autowired
  //  private RedisProperties redisProperties;
    @Autowired
    private Environment env;
    /**
     * 单机模式
     * @return
     *//*
    @Bean
    public RedissonClient redissonClient() {
        RedissonClient redissonClient;
        Config config = new Config();
        String url = "redis://" + redisProperties.getHost() + ":" + redisProperties.getPort();
        SingleServerConfig serverConfig = config.useSingleServer()
                .setAddress(url)
                .setTimeout(3000)
                .setDatabase(redisProperties.getDatabase())
                .setConnectionPoolSize(64)
                .setConnectionMinimumIdleSize(50);
        if (StringUtils.isNotBlank(redisProperties.getPassword())) {
            serverConfig.setPassword(redisProperties.getPassword());
        }
        try {
            redissonClient = Redisson.create(config);
            return redissonClient;
        } catch (Exception e) {
            log.error("RedissonClient init redis url:[{}], Exception:", url, e);
            return null;
        }
    }*/
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson() throws IOException {
        Config config = Config.fromYAML(new ClassPathResource("application-single-"+env.getActiveProfiles()[0]+".yml").getInputStream());
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }
}

 

Pruebe el código de bloqueo distribuido

@Autowired
private RedissonClient redissonClient; 

public Object testRedisLoak(){
        Boolean result=false;
        final String lockKey= "RedissonLock";
        RLock lock=redissonClient.getLock(lockKey);
        try {
            //TODO:第一个参数30s=表示尝试获取分布式锁,并且最大的等待获取锁的时间为30s
            //TODO:第二个参数10s=表示上锁之后,10s内操作完毕将自动释放锁
            Boolean cacheRes=lock.tryLock(10,10, TimeUnit.SECONDS);
            return cacheRes;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //TODO:释放锁
            lock.unlock();
        }
        return result;
    }

 

 

Empezar a integrar el modo de anotación

Anotación de definición: RsionLock


import org.springframework.core.annotation.AliasFor;

import java.lang.annotation.*;

/**
 * Description:
 *
 * <p>
 *      分布式锁的注解
 * </p>
 * 在同一个注解中成对使用即可,比如示例代码中,value和path就是互为别名。
 * 但是要注意一点,@AliasFor标签有一些使用限制,但是这应该能想到的,比如要求互为别名的属性属性值类型,默认值,都是相同的,互为别名的注解必须成对出现,比如value属性添加了@AliasFor(“path”),
 * 那么path属性就必须添加@AliasFor(“value”),另外还有一点,互为别名的属性必须定义默认值。
 *
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface RsionLock {

    /**
     * 支持spring El表达式
     * 锁的资源,key。
     */
    //@AliasFor("value")
    String key() default "";

    /**
     *  支持spring El表达式
     * 锁的资源,value。
     * 如果不为空这回拼接key
     */
    //@AliasFor("key")
    String value() default "";

    /**
     * 持锁时间,单位:秒
     * TODO:10s=表示上锁之后,10s内操作完毕将自动释放锁
     */
    long lockTime() default 10;

    /**
     * 当获取失败时候动作
     */
   // LockFailAction action() default LockFailAction.CONTINUE;

    /*public enum LockFailAction{
     *//** 放弃 *//*
        GIVEUP,
        *//** 继续 *//*
        CONTINUE;
    }*/


    /**
     * 等待时间,单位:秒
     * TODO:10s=表示尝试获取分布式锁,并且最大的等待获取锁的时间为10s
     * @return
     */
    int waitTime() default 10;

    /**
     * 是否自动延期机制
     * 默认不自动延期
     * @return
     */
    boolean isWatchDog() default false;

    /**
     * 延期时间 单位:秒
     * @return
     */
    int watchDogTime() default 2;
}

Definir el aspecto de la anotación LockAspectConfiguration


import com.dzhjj.dzhjjapi.annotations.RsionLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: heng
 * @Date: 2020/12/3 17:50
 * @Description: LockAspectConfiguration
 * @Version 1.0.0
 */
@Slf4j
@Aspect
@Configuration
public class LockAspectConfiguration {

    private ExpressionParser parser = new SpelExpressionParser();

    private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();


    @Autowired
    private RedissonClient redissonClient;

    /**
     * 定义切入点
     */
    @Pointcut("@annotation(com.dzhjj.dzhjjapi.annotations.RsionLock)")
    private void lockPoint() {
    }

    /**
     * 环绕通知
     *
     * @param pjp pjp
     * @return  方法返回结果
     * @throws Throwable throwable
     */
    @Around("lockPoint()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        RsionLock lockAction = method.getAnnotation(RsionLock.class);
        String logKey = getLogKey(lockAction, pjp, method);
        RLock lock=redissonClient.getLock(logKey);
        //TODO:第一个参数30s=表示尝试获取分布式锁,并且最大的等待获取锁的时间为30s
        //TODO:第二个参数10s=表示上锁之后,10s内操作完毕将自动释放锁
        Boolean cacheRes=lock.tryLock(lockAction.waitTime(),lockAction.lockTime(), TimeUnit.SECONDS);
        if (!cacheRes) {
            log.debug("get lock failed : " + logKey);
            return null;
        }
        //得到锁,执行方法,释放锁
        log.debug("get lock success : " + logKey);
        try {
            return pjp.proceed();
        } catch (Exception e) {
            log.error("execute locked method occured an exception", e);
        } finally {
           lock.unlock();
           log.debug("release lock : " + logKey + (cacheRes ? " success" : " failed"));
        }
        return null;
    }

    /**
     * 获得分布式缓存的key
     *
     * @param lockAction 注解对象
     * @param pjp        pjp
     * @param method     method
     * @return String
     */
    private String getLogKey(RsionLock lockAction, ProceedingJoinPoint pjp, Method method) {
        String key = lockAction.key();
        String value = lockAction.value();
        Object[] args = pjp.getArgs();
        return parse(key, method, args) + "_" + parse(value, method, args);
    }

    /**
     * 解析spring EL表达式
     *
     * @param key    key
     * @param method method
     * @param args   args
     * @return parse result
     */
    private String parse(String key, Method method, Object[] args) {
        String[] params = discoverer.getParameterNames(method);
        if (null == params || params.length == 0 || !key.contains("#")) {
            return key;
        }
        EvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < params.length; i++) {
            if (null != args[i]){
                context.setVariable(params[i], args[i]);
            }else {
                context.setVariable(params[i],"");
            }
        }
        return parser.parseExpression(key).getValue(context, String.class);
    }

}

 

El código de prueba debe probarse en la capa de servicio. Yo lo probaré en la capa del controlador.

 @RsionLock(key = "TestNewController_readlock", value = "#key",lockTime = 6,waitTime = 6)
  @RequestMapping("/readlock")
  public Object readlock(String key){
      return key;
  }

Visitar URL

http: // localhost: 8010 / dzhjjapi / testnew / readlock? key = test

ps: después de acceder al bloqueo de prueba, puede aumentar un poco el tiempo, comentar el bloqueo de liberación  y luego mirar el estado de retención de redis y el acceso a la URL que mantiene el estado de bloqueo. El principal es el tiempo para esperar el bloqueo y el otro es el momento de abrir la cerradura.

resultado

Supongo que te gusta

Origin blog.csdn.net/qq_39313596/article/details/110630733
Recomendado
Clasificación