Comentarios de Dark Horse 05 Bloqueo distribuido 1 Bloqueo mutex y tiempo de caducidad

Capítulo práctico 09. Principios básicos de bloqueo distribuido y comparación de diferentes métodos de implementación_bilibili_bilibili

1. Cerradura distribuida

Debido a que el bloqueo sincronizado dentro de la jvm no puede compartir el monitor de bloqueo entre diferentes jvm, se necesita un bloqueo fuera de la jvm para compartir.

2.redis setnx bloqueo mutex

Sólo bloquea y desbloquea

2.1 No liberar el bloqueo puede provocar un punto muerto

setnx de Redis no liberará automáticamente el bloqueo. Si el servicio deja de funcionar después del bloqueo, es posible que el bloqueo no se libere y quede bloqueado.

Por lo tanto, es necesario agregar un tiempo de vencimiento al bloqueo.

2.2 Garantizar la atomicidad de los tiempos de bloqueo y caducidad.

Utilice los parámetros set + para configurar el tiempo de bloqueo y vencimiento al mismo tiempo para garantizar que no haya un punto muerto debido al tiempo de inactividad antes de que se establezca el tiempo de vencimiento.

Versión definitiva:

Hasta ahora, el bloqueo distribuido se ha completado básicamente, pero aún se puede mejorar.

2.3 ¿Se bloquean otros hilos después de una falla?

Generalmente se utilizan métodos sin bloqueo , mientras que los métodos de bloqueo desperdician CPU y son problemáticos de implementar.

El método de bloqueo significa que si encuentras a alguien más usando el candado, seguirás esperando.

No bloqueo significa que si alguien más toma el candado, yo regresaré.

3. Implementar el bloqueo distribuido redis set nx 

 

3.1 Obtener el bloqueo distribuido de Redis

private String name; //业务名字
    private StringRedisTemplate stringRedisTemplate;

    private static final String KEY_PREFIX = "lock:"; //规范名字
    private static final String ID_PREFIX = UUID.randomUUID() + "-";
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;

    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean tryLock(long timeoutSec) {
        //获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //获取锁 set  key value  NX  EX 过期时间
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);  //防拆箱空指针
    }

3.2 Liberar el bloqueo distribuido de Redis

4. La empresa utiliza bloqueos distribuidos de Redis

En el negocio de creación de pedidos, cambie el bloqueo sincronizado a un bloqueo distribuido implementado por usted mismo (adquisición + desbloqueo)

5. El bloqueo del servicio conduce al problema de la eliminación accidental de bloqueos distribuidos (que determina la identificación del subproceso del bloqueo)

La empresa 1 está bloqueada durante demasiado tiempo, lo que hace que el bloqueo caduque y se elimine automáticamente.

5.1 Solución: Para determinar si el identificador de subproceso es suyo, necesita un identificador de subproceso único a nivel mundial.

 El número de subprocesos dentro de cada jvm es un número creciente, pero los números de subprocesos pueden entrar en conflicto entre diferentes jvm, por lo que es necesario encontrar una manera de distinguir los subprocesos no solo dentro de la jvm sino también entre jvm.

Uuid es un código de identificación único que puede garantizar que el uuid de diferentes servicios (jvm) debe ser diferente.

Por lo tanto, use uuid + jvm ID de subproceso interno para identificar de forma única los subprocesos en todos los jvm.

Divulgación científica: introducción y uso del identificador único universal UUID - Zhihu (zhihu.com)

5.1.1 Implementación (se agrega un juicio de identificación de hilo único al bloquear y soltar)

5.2 Otro problema de eliminación accidental (el bloqueo se bloquea y se pierde después de que se realiza el juicio y luego se libera. Es necesario garantizar la atomicidad de la ID del hilo y el bloqueo liberado)

Combate práctico Capítulo 15. El script distribuido Lock-Lua resuelve el problema de atomicidad de múltiples comandos_bilibili_bilibili

5.2.1 Garantizar la atomicidad - script lua

Llame a la función de llamada proporcionada por redis y pase los parámetros del comando redis

Después de dejar los bits de parámetros en blanco para pasar parámetros:

 5.2.2 java llama al script lua

 Lea el archivo Lua con anticipación para evitar lecturas frecuentes y llámelo más tarde.

Para mantener la atomicidad de determinar la ID del hilo y liberar el bloqueo al liberar el bloqueo, reescriba el método de desbloqueo.

6.Código final de este tipo

package com.hmdp.utils;

import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class SimpleRedisLock implements ILock {

    private String name; //业务名字
    private StringRedisTemplate stringRedisTemplate;

    private static final String KEY_PREFIX = "lock:"; //规范名字
    private static final String ID_PREFIX = UUID.randomUUID() + "-";
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;

    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
        UNLOCK_SCRIPT.setResultType(Long.class);
    }

    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean tryLock(long timeoutSec) {
        //获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //获取锁 set  key value  NX  EX 过期时间
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);  //防拆箱空指针
    }

    @Override
    public void unlock() {
        //调用lua脚本
        stringRedisTemplate.execute(UNLOCK_SCRIPT,
                Collections.singletonList(KEY_PREFIX + name),
                ID_PREFIX + Thread.currentThread().getId()
        );
    }

    /*
    *
    @Override
    public void unLock() {
        //获取线程标识
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //获取锁中的标识
        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
        //判断标识是否一致
        if (threadId.equals(id)) {
            //释放锁
            stringRedisTemplate.delete(KEY_PREFIX + name);
        }
    }
    * */
}

Supongo que te gusta

Origin blog.csdn.net/m0_50973548/article/details/135020579
Recomendado
Clasificación