Directorio de artículos
Prefacio
Como servicio de coordinación distribuida, Zookeeper proporciona algunos servicios básicos para sistemas distribuidos, como servicios de nombres, gestión de configuración, sincronización, etc., lo que permite a los desarrolladores manejar problemas distribuidos más fácilmente.
En los sistemas distribuidos, la coordinación es una tarea crítica. Por ejemplo, cómo permitir que un grupo de procesos o máquinas independientes sepan qué tareas deben realizar, cómo sincronizar su estado con otros procesos o máquinas y cómo manejar fallas o excepciones. Todos estos problemas los resuelve Zookeeper.
Este artículo le brindará una comprensión profunda de la implementación interna de Zookeeper, comenzando por sus diversos componentes e interfaces, y analizando sus principios de funcionamiento e ideas de diseño. Espero que en el proceso de lectura de este análisis del código fuente, pueda tener una comprensión profunda de los principios de funcionamiento y las ideas de diseño de Zookeeper, para que pueda utilizarlo mejor para resolver sus problemas distribuidos.
El cuidador del zoológico comienza
No importa qué código estemos viendo, debemos comenzar desde el punto de entrada del programa para que podamos comprender mejor la estructura general y el proceso operativo.
A través zkServer.sh
del script, puede ver que la clase de inicio es QuorumPeerMain
.
Puede ver que el inicio de Zookeeper se inicia a través del método principal y, finalmente, runFromConfig
se llama al método para configurar e iniciar un nodo del clúster de ZooKeeper.
El código anterior establece QuorumPeer
las rutas del directorio de instantáneas y el registro de transacciones, la selección del algoritmo de elección, la ID del servidor especificada, el ciclo de reloj definido y la instancia de guardado de datos y otras configuraciones. Además, se crean clases de fábrica (NIO/Netty) para conexiones de servidor y cliente. Finalmente, llame al método de inicio para iniciar el servicio.
En start
, se completan principalmente las siguientes cuatro tareas:
- Cargue datos del disco a la memoria para proporcionar las reservas de datos necesarias para el procesamiento y la respuesta posteriores.
- Establezca un Socket para manejar la solicitud del cliente e implementar la comunicación e interacción con el cliente.
- Realicé los preparativos para la elección del Líder y determiné el algoritmo electoral. .
- Llevar a cabo la elección de líder y monitorear el estado de los nodos y procesar en consecuencia, detectar y manejar fallas de nodos o estados anormales de manera oportuna y garantizar la confiabilidad y estabilidad de todo el sistema.
Cargar datos del disco
ZooKeeper opera a nivel de memoria. Para garantizar la confiabilidad, los datos se conservarán en archivos en forma de registros de transacciones, por lo que los datos se cargan en la memoria al inicio.
El objetivo principal de este código es cargar la base de datos de Zookeeper desde el disco a la memoria y verificar y procesar epoch
la información relacionada. getDataTree().lastProcessedZxid
Esta línea de código obtiene la última transacción que se ha procesado en el servidor ZooKeeper zxid
, ZxidUtils.getEpochFromZxid(lastProcessedZxid)
esta línea de código la obtiene zxid
de epoch
: readLongFromFile(CURRENT_EPOCH_FILENAME)
Esta línea de código lee la información actual del archivo epoch
. Si zxid
el valor de pertenencia epoch
es menor que el actual epoch
, IOException
se generará una excepción.
Comunicación e interacción con los clientes.
cnxnFactory
El servidor está establecido Socket
para recibir y procesar solicitudes de clientes. El código en la imagen a continuación es NIOServerCnxnFactory
y hay otro NettyServerCnxnFactory
. La opción NIO
aún Netty
se puede configurar en el archivo de configuración. Si está familiarizado con NIO
él, puede leer la columna de programación de redes.Netty
El núcleo es el procesamiento de las solicitudes de los clientes, existen los siguientes procesadores:
CommitProcessor
Es el procesador de envío de transacciones, que esperará la votación de la Propuesta en el clúster hasta que se pueda enviar la Propuesta.SyncRequestProcessor
Responsable de persistir las solicitudes de transacciones (solicitudes de escritura) en el disco local.AckRequestProcessor
Es un procesador exclusivo del Líder. Su principal responsabilidad esSyncRequestProcessor
enviar comentarios ACK al recopilador de votos de la Propuesta después de que el procesador completa el registro del registro de transacciones, para notificar al recopilador de votos que el servidor actual ha completado el registro del registro de transacciones para la Propuesta.FollowerRequestProcessor
Este procesador puede ser responsable de procesar las solicitudes de lectura y escritura del cliente, y reenviarlas a otros procesadores para su procesamiento, como reenviar solicitudes de escritura al nodo líder.SendAckRequestProcessor
Responsable de enviar un mensaje de reconocimiento (ACK) al nodo líder para notificarle que la solicitud ha sido procesada.
Preparación para las elecciones de líderes
startLeaderElection
El llamado principal es createElectionAlgorithm
crear el administrador de conexión de red entre clusters QuorumCnxManager
y seleccionar un algoritmo de elección, este se configura a través del archivo de configuración y el valor predeterminado es FastLeaderElection
.
Procesamiento del estado del nodo
super.start()
QuorumPeer.java
Los métodos de la clase de ejecución run()
se utilizan principalmente para monitorear y procesar el estado del nodo.
Cuando se inicia Zookeeper, se encuentra en el primer Looking
estado. A través de la elección, uno de los servidores se convierte en Líder y los otros servidores se convierten en Seguidores. Si el código es difícil de entender, puedes echar un vistazo al protocolo ZAB .
Resumir
Al analizar profundamente el código fuente, noté su simplicidad y su diseño inteligente de implementación funcional. Aunque la funcionalidad que maneja el código es bastante compleja, la simplicidad de la organización y el estilo de codificación hacen que sea bastante fácil de leer y comprender. Se presta especial atención al uso extensivo de la programación de redes y al cuidadoso tratamiento de la seguridad y optimización de las conexiones. También está la forma en que su protocolo ZAB resuelve inteligentemente el problema de la coherencia distribuida. Estos delicados conceptos de diseño sin duda proporcionan una referencia valiosa para el desarrollo de sistemas distribuidos. Espero integrar estas ideas en el código en trabajos futuros.