Una instancia única de Redis implementa un bloqueo distribuido

Una instancia única de Redis implementa un bloqueo distribuido

referencia:

Documentación oficial china de Redis: http://www.redis.cn/topics/distlock.html

Hay una condición de carrera.

Seguridad y seguridad en vivo

Según nuestras ideas y diseño, el algoritmo solo necesita tener tres características para lograr un bloqueo distribuido mínimo garantizado .

  1. Propiedad de seguridad: Exclusiva (mutuamente excluyente). En cualquier momento, sólo un cliente mantiene el candado.

  2. Propiedad de vivacidad A: sin punto muerto. Incluso si el cliente que mantiene el bloqueo falla o la red se divide, aún se puede adquirir el bloqueo.

  3. Propiedad de vivacidad B: Tolerancia a fallos. Mientras la mayoría de los nodos de Redis estén activos, los clientes pueden adquirir y liberar bloqueos.

Utilice el comando para obtener el bloqueo:

SET resource_name my_random_value NX PX 30000

La clave debe ser única globalmente, y si el valor es una cadena aleatoria, puede usar UUID (puede ser el mismo, es mejor usar un bloqueo de giro para generarlo), porque luego deberá determinar si el valor es su propio valor para eliminar la clave

eliminar bloqueo

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

1. Introducir dependencias

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis Lettuce 模式 连接池 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

Configurar redistribución

# 配置redis
spring.redis.host=47.107.243.178
spring.redis.port=6379
#spring.redis.password=123456
spring.redis.database=0
spring.redis.timeout=3000ms
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait=3000ms
# 连接池中的最大空闲连接(负数没有限制)
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0

2. Escribe RedisLock

/**
 * redis 单体 分布式锁
 */
@Component
public class RedisLock {
    
    

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 加锁
     * */
    public boolean tryLock(String key, String value) {
    
    
        int expirationTIME = 10;
        Boolean isLocked = stringRedisTemplate.opsForValue().
                setIfAbsent(key, value, expirationTIME, TimeUnit.SECONDS);
        if (isLocked == null) {
    
    
            return false;
        }
        return isLocked;
    }
    public boolean tryLock(String key, String value,int  expirationTime,TimeUnit timeUnit) {
    
    
        Boolean isLocked = stringRedisTemplate.opsForValue().
                setIfAbsent(key, value, expirationTime, timeUnit);
        if (isLocked == null) {
    
    
            return false;
        }
        return isLocked;
    }

    /**
     * 解锁
     * */
    public Boolean unLock(String key, String value) {
    
    
        // 执行 lua 脚本
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        // 指定 lua 脚本
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/unLock.lua")));
        // 指定返回类型
        redisScript.setResultType(Long.class);
        // 参数一:redisScript,参数二:key列表,参数三:arg(可多个)
        Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), value);
        return result != null && result > 0;
    }

}

3. Anotación

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RedLock {
    
    
    //调用接口的名称
    String key();

    // 过期时间
    int expiration() default 10;

    // 时间类型
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

4. AOP configura el bloqueo distribuido de redis

/**
 * redis 分布式锁 aop
 * */
@Aspect
@Slf4j
@Component
public class RedisLockAspect {
    
    

    @Autowired
    RedisLock redisLock;

    /**
     * 配置切入点
     */
    @Pointcut("@annotation(cn.patrick.adminpatrick.admin.annotation.RedLock)")
    public void redisLockPointcut() {
    
    
        // 该方法无方法体,主要为了让同类中其他方法使用此切入点
    }

    @Around("redisLockPointcut()")
    public Object redisLockAround(ProceedingJoinPoint  joinPoint) throws Throwable {
    
    
        Object result = null;
        // 获取注解上的值
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        RedLock redLockAnnotation = method.getAnnotation(RedLock.class);
        String value = UUID.randomUUID().toString().replace("-", "");
        String key = redLockAnnotation.key();
        int expiration = redLockAnnotation.Expiration();
        Random random = new Random();
        try {
    
    
            // 自旋锁
            while (true) {
    
    
                if (redisLock.tryLock(key,value,expiration,TimeUnit.SECONDS)) {
    
    
                    //log.info("[{}]成功获取锁", key);
                    break;
                }
                //log.info("[{}]获取锁失败",key);
                int sleepTime = random.nextInt(500);
                Thread.sleep(sleepTime);
            }
            result = joinPoint.proceed();

        } catch (Exception e) {
    
    
            throw new RuntimeException(e.getMessage());
        } finally {
    
    
            redisLock.unLock(key,value);
        }
        return result;
    }
}

5. Prueba

Supongo que te gusta

Origin blog.csdn.net/Dig_hoof/article/details/108444246
Recomendado
Clasificación