Red de conocimiento informático - Problemas con los teléfonos móviles - ¿Cómo implementa Zookeeper bloqueos distribuidos?

¿Cómo implementa Zookeeper bloqueos distribuidos?

1. Utilice la singularidad de los nombres de los nodos para implementar bloqueos compartidos.

La estructura de nodos abstraída por ZooKeeper es una pequeña estructura de directorios en forma de árbol similar al sistema de archivos Unix. El mecanismo ZooKeeper estipula que solo puede haber un nombre de archivo único en el mismo directorio. Por ejemplo: lo creamos en el directorio Zookeeper/directorio de prueba. Dos clientes crean un nodo llamado Lock y solo uno puede tener éxito.

Idea de algoritmo: aprovechando la singularidad del nombre, durante la operación de bloqueo, todos los clientes solo necesitan crear el nodo /test/Lock juntos. Solo uno de los nodos se crea con éxito y el nodo es exitoso. se obtiene la cerradura. Al desbloquear, simplemente elimine el nodo /test/Lock y los clientes restantes ingresan a la competencia para crear el nodo nuevamente hasta que todos los clientes obtengan el bloqueo.

Basado en el mecanismo anterior, el proceso del *** algoritmo de bloqueo compartido que utiliza el mecanismo de unicidad del nombre de nodo se muestra en la figura:

Esta *** implementación de bloqueo compartido es De acuerdo con nuestro concepto múltiple habitual de subprocesos que compiten por bloqueos y utilizan la singularidad de los nombres de nodos, es simple y confiable.

Es fácil ver en el algoritmo anterior que, dado que el cliente recibirá la notificación de que /test/Lock se ha eliminado al mismo tiempo y volverá a participar en la competencia para crear nodos, hay un " fenómeno de rebaño atronador".

La lista de rendimiento del uso de este método para probar cerraduras es la siguiente:

Resumen: la corrección y confiabilidad de esta solución están garantizadas por el mecanismo ZooKeeper y es fácil de implementar. . La desventaja es que producirá un efecto de "rebaño atronador". Si muchos clientes están esperando un bloqueo, todos los clientes se despertarán cuando se libere el bloqueo y solo un cliente obtendrá el bloqueo.

2. La práctica general de utilizar nodos secuenciales temporales para lograr bloqueos compartidos.

En primer lugar, permítanme presentarles que hay un nodo en Zookeeper llamado nodo secuencial, de ahí el nombre. Si estamos en/ Crea 3 nodos en el directorio lock/. El clúster ZooKeeper creará nodos en el orden en que fueron creados. Los nodos son /lock/0000000001, /lock/0000000002 y /lock/0000000003.

También hay un nodo llamado nodo temporal en ZooKeeper. El nodo temporal es creado por un cliente. Cuando el cliente se desconecta del clúster de ZooKeeper, el nodo abierto se elimina automáticamente.

Utilizando las dos características anteriores, echemos un vistazo a la lógica básica para obtener bloqueos distribuidos:

El cliente llama al método create() para crear un archivo llamado "locknode/guid -lock -" nodo, cabe señalar que el tipo de creación del nodo aquí debe establecerse en EPHEMERAL_SEQUENTIAL.

El cliente llama al método getChildren("locknode") para obtener todos los nodos secundarios creados y, al mismo tiempo, registra un observador para la notificación de cambio de nodo secundario en este nodo.

Después de que el cliente obtiene las rutas de todos los nodos secundarios, si descubre que el nodo que creó en el paso 1 tiene el número de secuencia más pequeño entre todos los nodos, se considera que el cliente ha obtenido el bloqueo.

Si descubre que no es el más pequeño de todos los nodos secundarios en el paso 3, significa que aún no ha obtenido el bloqueo y comenzará a esperar hasta la siguiente notificación de cambio de nodo secundario, y luego Vaya al nodo secundario y determine si desea adquirir el bloqueo.

El proceso de liberar el bloqueo es relativamente simple, simplemente elimina el nodo hijo que creaste.

La implementación anterior de bloqueos distribuidos generalmente puede satisfacer las necesidades de los bloqueos de competencia de clústeres distribuidos generales. El escenario general mencionado aquí se refiere a un tamaño de clúster pequeño, generalmente de 10 máquinas.

Sin embargo, si pensamos en la lógica de implementación anterior, podemos encontrar fácilmente un problema en el Paso 4, "Obtenga todos los subpuntos y determine si el nodo que creó ya es el nodo con la secuencia más pequeña. número". Este El proceso, en todo el proceso de competencia de bloqueo distribuido, se ejecuta repetidamente, y la mayoría de los resultados de la ejecución son para determinar que no es el nodo con el número de secuencia más pequeño, por lo que continúa esperando la siguiente notificación. Esto obviamente no parece muy científico.

El cliente recibe demasiadas notificaciones de eventos que son irrelevantes para sí mismo sin ningún motivo. Esto tendrá un gran impacto en el rendimiento del servidor si la escala del clúster es grande y si los clientes de varios nodos se desconectan al mismo tiempo. Esta vez, el servidor enviará una gran cantidad de notificaciones de eventos a otros clientes; este es el llamado efecto de rebaño atronador. La causa fundamental de este problema es que no se identifica el verdadero enfoque del cliente.

Revisemos el proceso de competencia de bloqueo distribuido anterior. Su lógica central es determinar si tiene el número de secuencia más pequeño entre todos los nodos. Por lo tanto, es fácil imaginar que el creador de cada nodo solo necesita prestar atención al nodo con un número de serie menor que el suyo.

3. Implementación mejorada de bloqueos compartidos utilizando nodos secuenciales temporales

La siguiente es la implementación mejorada de bloqueos distribuidos. La única diferencia con la implementación anterior es que el diseño aquí es que cada uno. El competidor de bloqueo solo necesita prestar atención a si existe un nodo debajo del nodo "locknode" con un número de secuencia menor que él.

Idea de algoritmo: para operaciones de bloqueo, todos los clientes pueden ir al directorio /lock para crear nodos secuenciales temporales si el cliente creado encuentra que el número de serie del nodo que crea es el más pequeño en /lock. / nodo de directorio, se obtiene el bloqueo. De lo contrario, supervise los nodos más pequeños que el número de secuencia del nodo que creó (el nodo más grande y más pequeño que el nodo que creó) y ingrese en espera.

Para la operación de desbloqueo, solo necesitas eliminar los nodos que creaste.

El flujo del algoritmo específico se muestra en la siguiente figura:

Los resultados de las pruebas utilizando el algoritmo anterior se muestran en la siguiente tabla:

Este algoritmo únicamente monitorea los nodos creados por él mismo. Los nodos con números de secuencia pequeños (el nodo más grande es más pequeño que él) no sufren un "impacto" cuando el nodo que actualmente adquiere el bloqueo lo libera.

Resumen El uso de nodos secuenciales temporales para implementar un mecanismo de bloqueo distribuido es en realidad una implementación de colas en el orden de creación. Esta solución es muy eficiente y evita el efecto de "rebaño atronador". Varios clientes esperan el bloqueo al mismo tiempo. Cuando se libera el bloqueo, solo se despierta un cliente.

4. Uso de la colección de animales

En realidad, es una encapsulación de la solución 3. No es necesario que escriba el código usted mismo. Úselo directamente.

Menagerie implementa una versión distribuida del paquete java.util.concurrent basada en Zookeeper. Esta encapsulación es una abstracción de varios escenarios de uso de coherencia distribuida con una granularidad mayor. El más básico y comúnmente utilizado es la implementación de un bloqueo distribuido: org.menagerie.locks.ReentrantZkLock, que implementa bloqueos distribuidos a través de la función de ordenamiento global de ZooKeeper y el soporte de znode de tipo EPHEMERAL_SEQUENTIAL. El método específico es: cada hilo que intenta obtener el bloqueo en un cliente diferente crea un nodo EPHEMERAL_SEQUENTIAL bajo la misma ruta base. EPHEMERAL significa que se creará un znode temporal, que se eliminará automáticamente cuando se desconecte la conexión; SEQUENTIAL significa que se agregará automáticamente un sufijo globalmente único incrementado automáticamente a la ruta entrante como ruta final. Por lo tanto, ZK generará diferentes sufijos para diferentes solicitudes y devolverá rutas con los respectivos sufijos para cada solicitud. Debido a la naturaleza ordenada globalmente de ZK, no importa cómo lleguen las solicitudes del cliente una tras otra, el orden eventualmente se organizará en el ZKServer. Por lo tanto, el nodo secundario con el sufijo de incremento automático más pequeño corresponde a la primera solicitud válida que llega. en ZK. Luego, el cliente lee todos los nodos secundarios bajo la ruta base y los compara con la ruta devuelta por ZK. Cuando descubre que el número de sufijo del nodo secuencial que creó ocupa el primer lugar, piensa que ha obtenido el bloqueo; cree que ha obtenido el bloqueo. No se ha adquirido ningún bloqueo. En este momento, debe haber otros clientes/hilos concurrentes y no desconectados que crearon el nodo primero.