Pacote inicial de bloqueio de mola distribuído (Redisson)

Devido aos serviços de direitos comerciais, os mesmos dados podem ser operados por várias pessoas, por isso há um problema de concorrência por operações de recursos compartilhados.Se for controlado separadamente para os negócios, o custo é relativamente alto.O controle específico deve ser associado aos negócios. É fácil causar problemas de conflito de concorrência; portanto, compartilhe o início do bloqueio distribuído com base no AOP. Bem-vindo ao discutir juntos e sugestões de aprimoramentos.
Aqui, o MethodInterceptor do aop é usado para interceptação no nível do método, e todo o processo é concluído com anotações.

1. Notas principais

1. Inicie a anotação de configuração Redission Ativar anotação:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({RedissonConfiguration.class, LockMethodHelper.class})
public @interface EnableRedissonLock {

}

2. Se o método inicia o bloqueio

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedLock {

    String fieldKey();
}

3. Principais notas de costura:

@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface KeyParam {
    /**
     * 如果动态参数在command对象中,那么就需要设置columns的值为command对象中的属性名可以为多个,否则不需要设置该值
     * <p>例1:public void test(@KeyParam({"id"}) MemberCommand member)
     * <p>例2:public void test(@KeyParam({"id","loginName"}) MemberCommand member)
     * <p>例3:public void test(@KeyParam String memberId)
     */
    String[] columns() default {};

    /**
     * 获取锁等待时间
     *
     * @return 等待时间
     */
    long waitTime() default 1L;

    /**
     * 锁释放时间
     *
     * @return 自动释放时间
     */
    long leaseTime() default 5L;

}

Segundo, a configuração do Redisssion

1. Encapsulamento de parâmetros de configuração básica

@Setter
@Getter
@Configuration
@ConfigurationProperties(prefix = "spring.redisson")
public class RedissonProperties {

    /**
     * redis访问地址
     */
    private String address;
    /**
     * 配置模式
     */
    private String connectModel;
    /**
     * 密码
     */
    private String password;
    /**
     * 超时时间
     */
    private int timeout;
    /**
     *  连接池大小
     */
    private int connectionPoolSize;
    /**
     * Minimum idle Redis connection amount.
     */
    private int connectionMinimumIdleSize;
}

2. Configure e execute o RedissonClient disponível (modo independente atual):

@Configuration
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonConfiguration {

    @Bean
    @ConditionalOnMissingBean(RedissonProperties.class)
    @ConditionalOnProperty(prefix = "spring.redisson", value = {"address", "password"})
    RedissonClient redissonSingleClient(RedissonProperties redissonProperties) {
        Config config = new Config();
        config.useSingleServer()
            .setAddress(redissonProperties.getAddress())
            .setTimeout(redissonProperties.getTimeout())
            .setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
            .setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize())
            .setPassword(redissonProperties.getPassword());
        return Redisson.create(config);
    }


}

3. Método alvo:

@Slf4j
public class LockMethodHelper implements MethodInterceptor {


    public static final String BEFORE_KEY = "LOCK:";

    @Autowired(required = false)
    private RedissonClient redissonClient;

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Method method = methodInvocation.getMethod();
        Object[] args = methodInvocation.getArguments();
        //锁key
        String key = builderKey(method, args);
        // key不存在的情况下,不加锁处理,直接执行目标方法
        if (StringUtils.isEmpty(key)) {
            return methodInvocation.proceed();
        }
        RLock lock = redissonClient.getLock(key);
        try {
            if (lock.tryLock()) {
                // 通过反射机制调用目标方法
                return methodInvocation.proceed();
            } else {
                log.warn("资源竞争太大了,获取锁失败了,key为:{}", key);
                throw new LockException("人太多了,系统小哥已经处理不过来了...");
            }
        } catch (Exception e) {
            log.error("执行目标方法抛出异常,异常信息为:{}", e);
            throw e;
        } finally {
            lock.unlock();
        }
    }

    private String builderKey(Method method, Object[] args) throws NoSuchFieldException, IllegalAccessException {
        NeedLock needLock = method.getAnnotation(NeedLock.class);
        if (null == needLock) {
            return null;
        }
        // 锁的前半部分key
        String key = BEFORE_KEY + needLock.fieldKey();
        // 迭代全部参数的注解,根据使用KeyParam的注解的参数所在的下标,来获取args中对应下标的参数值拼接到前半部分key上
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation annotation : parameterAnnotations[i]) {
                // 当前参数的注解不包含keyparam
                if (!annotation.annotationType().getClass().isInstance(KeyParam.class)) {
                    continue;
                }
                // 当前参数的注解包含keyparam,获取注解配置的值
                String[] columns = ((KeyParam) annotation).columns();
                if (columns.length == 0) {
                    // 普通数据类型直接拼接
                    if (null == args[i]) {
                        log.error("动态参数不能为null!");
                        throw new RuntimeException("动态参数不能为null!");
                    }
                    key += args[i];
                } else {
                    // keyparam的columns值不为null,所以当前参数应该是对象类型
                    for (int j = 0; j < columns.length; j++) {
                        Class<? extends Object> clasz = args[i].getClass();
                        Field declaredField = clasz.getDeclaredField(columns[j]);
                        declaredField.setAccessible(true);
                        Object value = declaredField.get(args[i]);
                        key += value;
                    }
                }
            }
        }
        return key;
    }
}

Quarto, demonstração de caso de uso:

1. Configuração do intervalo de interceptação, o usuário configura o intervalo do pacote de interceptação

@EnableRedissonLock
@Configuration
public class DisturiteLockConfig {

    public static final String traceExecution = "execution(* com.workstation.biz.service..*.*(..))";

    @Autowired
    LockMethodHelper lockMethodHelper;


    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(traceExecution);
        // 配置增强类advisor
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(pointcut);
        advisor.setAdvice(lockMethodHelper);
        return advisor;
    }
}

2. Método:

   // 基于对象的使用
    @NeedLock(fieldKey = "user:uuid")
    public void userChange(@KeyParam(columns = {"uuid"}) UserDTO user) {

    }
// 基于字段的使用
   @NeedLock(fieldKey = "user:uuid")
    public void userChange(@KeyParam String uuid) {

    }

Insira a descrição da imagem aqui

Publicado 41 artigos originais · Gostei 14 · Visitantes 10.000+

Acho que você gosta

Origin blog.csdn.net/Yunwei_Zheng/article/details/104986463
Recomendado
Clasificación