Red de conocimiento informático - Problemas con los teléfonos móviles - Análisis detallado del bloqueo distribuido de Redis

Análisis detallado del bloqueo distribuido de Redis

Creo que todo el mundo comprende la función de los bloqueos, que es permitir que diferentes subprocesos o procesos operen de forma segura recursos compartidos sin conflictos.

Los más familiares son Synchronized y ReentrantLock, que pueden garantizar el funcionamiento seguro de recursos compartidos por diferentes subprocesos en el mismo programa jvm.

Pero en un sistema distribuido, este método falla; dado que el sistema distribuido tiene múltiples subprocesos, múltiples procesos y están distribuidos en diferentes máquinas, esto invalidará la estrategia de bloqueo de control de concurrencia de una sola máquina. este problema El problema requiere un mecanismo de exclusión mutua entre JVM para controlar el acceso a los recursos compartidos.

Hay tres métodos de implementación de bloqueos distribuidos comúnmente utilizados:

Este artículo explica principalmente la implementación de bloqueos distribuidos basados ​​en Redis.

La función principal de los bloqueos distribuidos es garantizar que solo un cliente pueda acceder a los recursos compartidos en cualquier momento.

Sabemos que redis tiene un comando NX de valor de clave SET, que solo se puede ejecutar con éxito cuando la clave no existe. Esto garantiza que solo uno de varios clientes pueda ejecutarse con éxito, lo que equivale a adquirir un. cerrar con llave.

Al liberar el bloqueo, sólo es necesario borrar la llave del llave.

La implementación anterior parece haber cumplido los requisitos, pero se olvidó de considerar los siguientes problemas en un entorno distribuido:

El mayor problema es que debido a problemas del cliente o de la red, The La clave en Redis no se elimina y el bloqueo no se puede liberar, por lo que otros clientes no pueden obtener el bloqueo.

En respuesta a la situación anterior, se utilizó el siguiente comando:

Utilice el comando PX para agregar un tiempo de vencimiento automático (30 segundos) a la clave para garantizar que incluso si el La liberación no se llama al método de bloqueo debido a circunstancias inesperadas, el bloqueo se liberará automáticamente y otros clientes aún podrán obtener el bloqueo.

Tenga en cuenta que el valor my_random_value establecido para esta clave es un valor aleatorio y se debe garantizar que este valor sea único en el cliente. El propósito de este valor es liberar el bloqueo de manera más segura.

Esto es para evitar eliminar bloqueos que otros clientes adquirieron con éxito. Considere la siguiente situación:

Por lo tanto, aquí se usa un valor aleatorio de my_random_value para garantizar que el cliente solo liberará el bloqueo que ha adquirido, es decir, solo eliminará la clave que estableció.

Este método de implementación tiene los siguientes problemas:

El capítulo anterior presentó los problemas de implementación simple. Introduzcamos cómo la implementación de Redisson resuelve estos problemas.

Centrarse principalmente en el método tryAcquireOnceAsync, que tiene tres parámetros:

El proceso principal del método:

El proceso de este método es el mismo que tryLock (tiempo de espera prolongado, tiempo de arrendamiento prolongado, unidad TimeUnit) El método es básicamente el mismo.

La diferencia entre este método y el método tryAcquireOnceAsync es que uno es el tiempo de vencimiento para adquirir el bloqueo y el otro es si se puede adquirir el bloqueo. Es decir, si el tiempo de vencimiento del bloqueo de adquisición es nulo, significa que el bloqueo ha sido adquirido, y si es diferente, significa que el bloqueo no ha sido adquirido.

Este método eventualmente se llamará al adquirir el bloqueo y el script lua interactúa con redis para implementar bloqueos distribuidos.

Primero analice los parámetros pasados ​​al script lua:

El proceso del script lua:

Para lograr una retención ilimitada del bloqueo, el bloqueo debe Se actualizará periódicamente cuando expire el tiempo.

Los atributos más importantes de esta clase son dos miembros:

Utilice una colección concurrente estática EXPIRATION_RENEWAL_MAP para almacenar el ExpirationEntry correspondiente a todos los bloqueos. Cuando hay un nuevo ExpirationEntry, se almacena. en la colección EXPIRATION_RENEWAL_MAP Cuando esté en uso, debe llamar al método renewExpiration para actualizar el tiempo de vencimiento.

Crear una tarea de tiempo de espera Tarea de tiempo de espera El tiempo de espera es internalLockLeaseTime / 3. Después de este tiempo, se llama al método renewExpirationAsync (threadId) para actualizar el tiempo de vencimiento del bloqueo.

Determine si el hilo actual mantiene el bloqueo, luego restablezca el tiempo de vencimiento y devuelva 1, lo cual es verdadero. De lo contrario, devuelve 0 o falso.

Libere el bloqueo llamando a unlockInnerAsync(threadId) para eliminar la clave en redis. Preste especial atención al hecho de que cuando ocurre una falla cuando el subproceso que no retiene el bloqueo lo libera, no es necesario llamar al método cancelExpirationRenewal para cancelar el tiempo, porque el bloqueo aún está retenido por otros subprocesos.

El valor pasado a este script lua:

El proceso de este script lua:

Se llama al método de suscripción de LockPubSub para suscribirse.

La función de este método es iniciar una suscripción a redis, pero para el mismo cliente (es decir, un sistema jvm) con el mismo bloqueo, solo se iniciará una suscripción y otros subprocesos del mismo cliente esperando el mismo bloqueo Se registrará en RedissonLockEntry.

Flujo del método:

Solo cuando el contador gt; = lo permita, se ejecutará el oyente de devolución de llamada, lo que tiene el efecto de controlar el funcionamiento del oyente.

Libera un volumen de control para que uno de los oyentes de devolución de llamada pueda ejecutarse.

Atributos principales:

El registro de comandos monitoreado en redis correspondiente a este proceso:

Porque el tiempo predeterminado del perro guardián es de 30 segundos y la actualización programada program El tiempo es 1/3 del tiempo de vigilancia, es decir, 10 segundos. El programa de muestra duerme durante 15 segundos, lo que provoca que se active la operación de tiempo de vencimiento del bloqueo de actualización.

Tenga en cuenta que el tiempo de rLock.tryLock(10, TimeUnit.SECONDS); debe establecerse mayor. Si el tiempo de espera es demasiado corto y menor que el tiempo del comando redis para adquirir el bloqueo, lo hará directamente. volver al fracaso en la adquisición del candado.

Al analizar el código fuente, comprendemos la distribución del modo Redisson y resolvemos los problemas de tiempo de vencimiento del bloqueo y reentrada. Sin embargo, en realidad no existe una solución para el problema del punto único de falla que puede existir en el propio Redis.

Basado en este problema, el autor de Redis propuso un algoritmo llamado Redlock. Sin embargo, este algoritmo en sí también tiene algunos problemas. Si desea obtener más información, consulte ¿Son seguros los bloqueos distribuidos basados ​​en Redis? /p>p>