Redis (3) - Distributed Lock consulta profundidad

Original: Redis (3) - Distributed Lock consulta profundidad

 


I. Introducción bloqueo distribuido

Bloqueo es una técnica utilizada para resolver múltiples hilos de ejecución acceder a un recurso compartido de errores o inconsistencias de herramientas en los datos.

Si se coloca un servidor en comparación con una casa , entonces el hilo es como en el interior del hogar , cuando quieren acceder a un comunes de recursos compartidos, tales como el tiempo de aseo, si no hay una cerradura de la puerta del baño ... Peor aún no ha instalado la puerta del baño. .. esto es será una cuestión de principio de ..

Equipada con una cerradura, usamos sean más tranquilidad, que es esencialmente el mismo tiempo permite sólo un uso doméstico .

Con el desarrollo del mundo de Internet, aplicaciones monolíticas ha vuelto cada vez más incapaces de satisfacer las necesidades complejas de alta concurrentes de Internet, moviéndose lentamente gire en la dirección de distribuir, y evolucionó lentamente en un mayor número de hogares . Por lo tanto Del mismo modo, es necesario introducir un bloqueo distribuido para resolver el problema del acceso concurrente a recursos compartidos entre las aplicaciones distribuidas.

¿Por qué necesitamos un bloqueo distribuido

En circunstancias normales, el uso descentralizado de bloqueo Hay dos escenarios principales:

  1. Evitar la repetición de la misma obra nodos diferentes : por ejemplo, el usuario ha realizado una acción pueden ser diferentes nodos envían varios mensajes;
  2. Para daños evitar en la corrección de los datos : el caso si dos nodos operan simultáneamente en los mismos datos pueden causar errores de datos o inconsistencias surgir;

Java forma común implementado

Por encima se utilizó una simple analogía ilustra la naturaleza de las cerraduras: una sola vez permite una operación de usuario . Por lo tanto, en teoría, ser capaz de satisfacer las necesidades de todas las herramientas que podemos utilizar (es decir, otras aplicaciones pueden ayudarnos a encerrados) :

  1. Basado en MySQL bloquea : sí MySQL tiene incorporado un bloqueo pesimista for updateclave, también puede lograr su propio bloqueo pesimista / optimista para lograr sus objetivos;
  2. Basado en Zookeeper ordenó nodo : Zookeeper permita la orden temporal para crear un nodo secundario, de modo que cuando el cliente obtiene la lista de nodos, se puede determinar el número de serie de la actual lista de nodos secundarios es capaz de adquirir un bloqueo;
  3. Basándose en los Redis de un único subproceso : Debido Redis es de un solo subproceso, el comando ejecutado de una manera en serie, y la imagen misma se proporciona SETNX(set if not exists)de tal manera que las instrucciones, es en sí mismo son mutuamente excluyentes;

Cada programa tiene sus propias ventajas y desventajas, tales como MySQL, aunque intuitivamente entender fácilmente, pero que necesitan para poner en práctica las consideraciones adicionales de bloqueo de tiempo de espera , además de la transacción y así sucesivamente, y limitan a la realización de la base de datos, por lo que no hemos discutido, centrándose en Redis.

Redis problemas de bloqueo distribuidos

1) tiempo de espera de bloqueo

Supongamos ahora que tenemos dos servicios paralelos AB, donde un servicio después de adquirir el bloqueo debido a una fuerza misteriosa desconocida repentinamente colgó , a continuación, los servicios de B nunca serán capaces de obtener un bloqueo:

Por lo que necesitamos más tiempo para establecer un tiempo de espera para asegurar la disponibilidad de servicios.

Pero luego vino otro problema: si la lógica entre el bloqueo y la liberación de la ejecución de bloqueo demasiado tiempo, por lo que más allá del límite de tiempo de espera de bloqueo , no habrá problemas. Porque esta vez la primera rosca mantiene el bloqueo caducado, y las áreas críticas de lógica no se ha realizado, mientras que el segundo hilo de antemano tiene la cerradura, lo que resulta en una sección crítica de código que no puede ser ejecución estrictamente serial .

Para evitar este problema, Redis distribuye tarea de bloqueo por no mucho tiempo . Si de verdad de vez en cuando hay un problema, causado por pequeños datos ilegibles pueden requerir intervención manual.

Hay un poco más seguro solución es para bloquear el valuevalor se establece en un número al azar , un número aleatorio es consistente si el primer partido cuando se libera el bloqueo, y luego retire la llave, esto es para asegurar que el hilo actual propietaria de la cerradura otros hilos no serán liberados a menos el bloqueo se debe a expirar y ser liberado automáticamente del servidor.

Pero el partido valuey sean eliminados keyen el Redis no es una operación atómica, no hay instrucciones atómicas de garantías similares, puede ser necesario el uso de la escritura Lua como esto de tratar, debido a Lua script puede garantizar la atomicidad instrucciones de ejecución .

GC puede conducir a problemas de seguridad: la ampliación de la discusión

Martin Kleppmann trabajó con el padre Antirez Redis Redis para lograr los problemas de seguridad de bloqueo distribuido realizadas discusión en profundidad, en las que existe un problema relacionado con la GC .

Los estudiantes están familiarizados con Java es, sin duda no es ajeno a la GC, GC se produce cuando el STW (Stop-a-El Mundo) , que en sí mismo es proteger a la ejecución normal del recolector de basura, pero puede causar los siguientes problemas:

Un servicio adquirido bloqueo y ponga el tiempo de espera, pero el servicio A y STW apareció mucho tiempo, lo que resulta en un tiempo de espera de bloqueo distribuido ha sido liberado, durante este período el servicio B adquirir el bloqueo, después de la finalización del servicio STW Un reanudó cerradura, lo que llevó al servicio de un servicio y B al mismo tiempo llegar a la cerradura , esta cerradura tiempo distribuido no es seguro.

No se limita a Redis, Zookeeper y MySQL tienen el mismo problema.

Coma más Zapatos para niños de melón, se puede visitar el siguiente sitio web y ver el padre de Antirez cómo dijo Redis: http://antirez.com/news/101

2) de un solo punto / multi-punto problema

Si Redis desplegado en modo autónomo, esto significa que cuando falla Redis, que hará que todo el servicio no está disponible.

Si se utiliza el modo de despliegue de maestro-esclavo, nos imaginamos un escenario de este tipo: Servicio A después de la aplicación de un bloqueo, si el host disminuye a medida que Redis, a continuación, el servicio B en el momento del bloqueo de la aplicación obtendrá a partir de ahí a este esclavo cerradura, con el fin de resolver este problema, los autores proponen un Redis bloqueo rojo RedLock algoritmo (Redission con Jedis) :

// 三个 Redis 集群
RLock lock1 = redissionInstance1.getLock("lock1");
RLock lock2 = redissionInstance2.getLock("lock2");
RLock lock3 = redissionInstance3.getLock("lock3");

RedissionRedLock lock = new RedissionLock(lock1, lock2, lock2);
lock.lock();
// do something....
lock.unlock();

Dos, Redis aplicación de bloqueo distribuido

Distribuido de bloqueo similar a "ocupar boxes", y SETNX(SET if Not eXists)la instrucción es una de esas operaciones y sólo puede ser ocupado un cliente, echamos un vistazo al código fuente (t_string.c / setGenericCommand) es:

// SET/ SETEX/ SETTEX/ SETNX 最底层实现
void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
    long long milliseconds = 0; /* initialized to avoid any harmness warning */

    // 如果定义了 key 的过期时间则保存到上面定义的变量中
    // 如果过期时间设置错误则返回错误信息
    if (expire) {
        if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != C_OK)
            return;
        if (milliseconds <= 0) {
            addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name);
            return;
        }
        if (unit == UNIT_SECONDS) milliseconds *= 1000;
    }

    // lookupKeyWrite 函数是为执行写操作而取出 key 的值对象
    // 这里的判断条件是:
    // 1.如果设置了 NX(不存在),并且在数据库中找到了 key 值
    // 2.或者设置了 XX(存在),并且在数据库中没有找到该 key
    // => 那么回复 abort_reply 给客户端
    if ((flags & OBJ_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||
        (flags & OBJ_SET_XX && lookupKeyWrite(c->db,key) == NULL))
    {
        addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
        return;
    }
    
    // 在当前的数据库中设置键为 key 值为 value 的数据
    genericSetKey(c->db,key,val,flags & OBJ_SET_KEEPTTL);
    // 服务器每修改一个 key 后都会修改 dirty 值
    server.dirty++;
    if (expire) setExpire(c,c->db,key,mstime()+milliseconds);
    notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id);
    if (expire) notifyKeyspaceEvent(NOTIFY_GENERIC,
        "expire",key,c->db->id);
    addReply(c, ok_reply ? ok_reply : shared.ok);
}

Al igual que como se describió anteriormente, de hecho, en la versión anterior de Redis debido SETNXy EXPIREno las instrucciones atómicas , por lo que habrá problemas con la ejecución.

Se puede pensar en el uso de transacciones Redis por resolver, pero no aquí, porque el EXPIREcomando depende de SETNXlos resultados de la transacción sin if-elseuna lógica ramificada, si SETNXno agarra la cerradura, EXPIREno debe ser ejecutado.

Para resolver este difícil problema, Redis comunidad de código abierto han surgido muchas nuevas biblioteca de bloqueo distribuido, con el fin de controlar este caos, y más tarde en la versión 2.8 Redis, mediante la adición de las SETinstrucciones de parámetros ampliados que SETNXpuedo y EXPIREejecución de la instrucción juntos:

> SET lock:test true ex 5 nx
OK
... do something critical ...
> del lock:test

Sólo tiene que cumplir con SET key value [EX seconds | PX milliseconds] [NX | XX] [KEEPTTL]el formato tal como, también gira a la derecha por debajo del documento oficial de referencia:

Además, los documentos oficiales son SETNXdocumentos mencionados tal idea: la clave SETNX correspondiente al valor ajustado en <actual Unix Time + bloqueo de tiempo de espera + 1> , de modo que cuando el otro acceso de cliente será capaz de juzgar por sí mismos si para obtener la siguiente valor es un bloqueo del formato descrito anteriormente.

la implementación del código

Jedis a continuación con referencia a la siguiente implementación analógica, el código de la llave es el siguiente:

private static final String LOCK_SUCCESS = "OK";
private static final Long RELEASE_SUCCESS = 1L;
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";

@Override
public String acquire() {
    try {
        // 获取锁的超时时间,超过这个时间则放弃获取锁
        long end = System.currentTimeMillis() + acquireTimeout;
        // 随机生成一个 value
        String requireToken = UUID.randomUUID().toString();
        while (System.currentTimeMillis() < end) {
            String result = jedis
                .set(lockKey, requireToken, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
            if (LOCK_SUCCESS.equals(result)) {
                return requireToken;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    } catch (Exception e) {
        log.error("acquire lock due to error", e);
    }

    return null;
}

@Override
public boolean release(String identify) {
    if (identify == null) {
        return false;
    }

    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    Object result = new Object();
    try {
        result = jedis.eval(script, Collections.singletonList(lockKey),
            Collections.singletonList(identify));
        if (RELEASE_SUCCESS.equals(result)) {
            log.info("release lock success, requestToken:{}", identify);
            return true;
        }
    } catch (Exception e) {
        log.error("release lock due to error", e);
    } finally {
        if (jedis != null) {
            jedis.close();
        }
    }

    log.info("release lock failed, requestToken:{}, result:{}", identify, result);
    return false;
}
  • Presupuesto de debajo de la referencia 3 , de los cuales hay RedLock aplicación y pruebas, interesado en los zapatos de los niños puedan perforar

lectura recomendada

  1. [Documento Oficial] con Redis Cerraduras Distribuido - https://redis.io/topics/distlock
  2. Redis [entrada] en este caso - Https://www.wmyskxz.com/2018/05/31/redis-ru-men-jiu-zhe-yi-pian/
  3. Redission - código fuente de Java Redis Cliente - https://github.com/redisson/redisson
  4. Escrito a mano un Jedis y JedisPool - https://juejin.im/post/5e5101c46fb9a07cab3a953a

material de referencia

  1. Bloqueo distribuido de nuevo alguien le pregunta, tirarlo en este artículo - https://juejin.im/post/5bbb0d8df265da0abd3533a5#heading-0
  2. [Documento Oficial] con Redis Cerraduras Distribuido - https://redis.io/topics/distlock
  3. [Serie] distribuido memoria caché distribuida bloqueo Redis lograr la postura correcta - https://www.cnblogs.com/zhili/p/redisdistributelock.html
  4. Redis análisis de origen y comentarios (nueve) --- lograr una cadena de comando (T_STRING) - https://blog.csdn.net/men_wen/article/details/70325566
  5. "Profundidad Aventura Redis" - producto Qian / adelante
  • En este artículo se ha incluido a mi programador Github creciente serie [Más de Java], el aprendizaje, más de Código, la bienvenida a la estrella: https://github.com/wmyskxz/MoreThanJava
  • Número Personal Público : wmyskxz, individuo dominio nombre del blog independiente : wmyskxz.com, se adhieren a la salida original, código de exploración a continuación se refieren, 2020, para crecer con usted!

Muchas gracias personas que pueden ver aquí , si cree que este artículo está bien escrito, que "no tengo corazón tres" pequeñas cosas , entonces, la búsqueda de pulgar hacia arriba, la búsqueda de atención, tratando de acción, encontrar un mensaje!

La escritura no es fácil, por su apoyo y reconocimiento, la creación es mi mayor motivación, vemos el próximo artículo!

Supongo que te gusta

Origin www.cnblogs.com/lonelyxmas/p/12514996.html
Recomendado
Clasificación