Red de conocimiento informático - Material del sitio web - Cómo implementar un clúster Redis de alta disponibilidad

Cómo implementar un clúster Redis de alta disponibilidad

Redis tiene estructuras de datos ricas, rendimiento ultraalto y un protocolo simple, por lo que puede usarse como la capa de almacenamiento en caché ascendente de la base de datos. Sin embargo, cuando se usa Redis a escala, está limitado en varios aspectos: memoria limitada de una sola máquina, presión de ancho de banda, problemas de punto único e incapacidad de expandirse dinámicamente.

Basado en la situación anterior, la solución de clúster de Redis es particularmente importante. Hay tres formas de hacer esto: clúster oficial de Redis, fragmentación a través de proxy y fragmentación del lado del cliente (clientes inteligentes). Cada uno de estos tres métodos tiene pros y contras.

Redis Cluster (oficial): aunque la versión oficial se lanzó durante más de un año, todavía carece de las mejores prácticas y el protocolo se ha modificado significativamente, lo que hace que no todos los clientes principales sean compatibles; algunos de ellos son Los clientes admitidos aún no se han verificado en entornos de producción a gran escala; el diseño descentralizado hace que todo el sistema esté altamente acoplado, lo que dificulta la realización de actualizaciones comerciales sencillas.

Los proxy son la forma más potente y eficaz de mejorar el rendimiento empresarial.

Proxy: actualmente, muchos clústeres de Redis convencionales utilizan servidores proxy, como Codis de código abierto. Esta solución tiene muchas ventajas porque admite el protocolo nativo Redis, por lo que no es necesario actualizar el cliente y es más amigable para los negocios. La actualización también es más sencilla ya que se pueden iniciar y actualizar varios agentes uno por uno. Pero la desventaja es que debido a un salto más, la sobrecarga de rendimiento promedio es de aproximadamente 30. Además, dado que el cliente nativo no puede vincular varios servidores proxy al mismo tiempo, aún se requiere la participación manual si el proxy conectado se bloquea. A menos que el cliente original esté encapsulado como un Smart Client, admite la reconexión con otros servidores proxy, pero esto también trae algunas desventajas del método de división del cliente. Además, aunque se pueden utilizar varios servidores proxy y se pueden agregar servidores proxy dinámicamente para mejorar el rendimiento, si todos los clientes están conectados a todos los servidores proxy, algunos servicios anormales pueden afectar a otros servicios. La configuración de servidores proxy separados para cada servicio también genera trabajo adicional para la implementación.

Elegimos la tercera opción, el cliente inteligente. La fragmentación del lado del cliente tiene mejor rendimiento y menor latencia en comparación con los servidores proxy. Por supuesto, esto tiene sus inconvenientes: la actualización requiere reiniciar el cliente y necesitamos mantener versiones en varios idiomas, pero nos gusta el alto rendimiento.

Permítanos presentarle nuestro clúster de Redis:

Descripción general:

Como se muestra en la Figura 0,

Nuestro clúster de Redis consta de cuatro roles:

Zookeeper: guarda las direcciones de todas las instancias en el clúster de Redis. La instancia de Redis escribe su propia dirección en una ruta específica de acuerdo con el protocolo. La instancia de Redis escribe su propia dirección en una ruta específica de acuerdo con el protocolo, y el cliente encuentra la dirección de la instancia de Redis para leer y escribir de acuerdo con el protocolo.

Instancia de Redis: modificamos el código fuente de Redis para que cuando Redis se inicie o cambie del maestro al esclavo, su dirección se escriba automáticamente en una ruta específica en zookeeper de acuerdo con la convención.

Sentinel: redis viene con una herramienta de conmutación maestro-esclavo. Usamos sentinel para lograr una alta disponibilidad del clúster.

Cliente inteligente: el cliente sigue la convención para encontrar la ubicación de escritura de la instancia de redis en ZooKeeper. Dependiendo de la cantidad de grupos en el clúster, realizará un cálculo hash consistente para determinar a qué grupo pertenece exclusivamente la clave y luego realizará operaciones en el repositorio maestro de ese grupo. El cliente configurará un monitor en ZooKeeper para notificarle cuando cambie el repositorio maestro de un grupo, y el cliente actualizará el repositorio maestro más reciente para el grupo correspondiente.

Nuestro clúster de Redis está organizado por empresas y diferentes empresas utilizan diferentes clústeres (es decir, existe una relación uno a uno entre la empresa y el clúster). Un clúster de Redis consta de varios grupos (un grupo consta de un par de instancias de Redis maestra y esclava).

En otras palabras, cuantos más grupos haya, más máquinas se podrán implementar y más memoria y ancho de banda se podrán utilizar. En la Figura 0, el clúster de Redis utilizado por esta empresa consta de 2 grupos, cada grupo consta de un par de instancias maestro-esclavo.

Conmutación por error

Como se muestra en la Figura 1,

Cuando se inicia redis, escribe su IP: Puerto en ZooKeeper. Cuando se inicia el modo maestro, su IP:Puerto se escribe en el nodo persistente /redis/business/groupname (o se crea si no existe). Al cambiar del modo maestro al modo esclavo, se crea un nodo temporal /redis/nombre comercial/nombre de grupo/esclavos/ip:puerto y se escribe su propia IP:puerto (o se elimina el mismo nodo que ya existe y se crea un nuevo nodo). se crea). En modo esclavo, se creará un nodo temporal /redis/nombre comercial/nombre de grupo/esclavo/IP:puerto al iniciar y escribirá su propia IP:puerto (si el mismo nodo ya existe, elimínelo primero y luego créelo). Al cambiar del modo esclavo al modo maestro, elimine el nodo temporal /redis/service/groupname/slaves/ip:port y escriba su propia IP:port en el nodo permanente /redis/service/groupname.

ZooKeeper conservará la IP de la instancia maestro-esclavo actualmente válida: información del puerto. En cuanto al proceso de cambio automático maestro-esclavo, se implementa a través del centinela que viene con redis. Actualmente está configurado para cambiar entre la instancia maestro-esclavo cuando el servidor maestro no responde durante más de 30 segundos. la instancia maestro-esclavo se activará a través de la serie de operaciones anterior para completar el cambio final.

El cliente determina a qué grupo pertenece una clave aplicando hash continuamente a todos los nombres de grupo bajo un nombre comercial determinado. Cuando el cliente se inicia, obtendrá la IP maestra y la IP esclava: puerto de todos los grupos bajo el nombre comercial dado de ZooKeeper y configurará el monitoreo en ZooKeeper (el propósito del monitoreo es notificar al cliente cuando los nodos en ZooKeeper cambien). . Si el cliente recibe una notificación de cambio de nodo de Zookeeper, recupera el último puerto I: maestro/esclavo y restablece la supervisión (la supervisión de ZooKeeper es única). De esta manera, el cliente puede conocer la última IP maestro-esclavo accesible: información del puerto en tiempo real.

Dado que todas nuestras instancias de Redis se almacenan en ZooKeeper como de costumbre, no necesitamos implementar el monitoreo por instancia. Hemos escrito una herramienta que puede obtener automáticamente información sobre todas las instancias de Redis a través de ZooKeeper y. monitorear CPU, qps, memoria, retardo maestro-esclavo, conmutación maestro-esclavo, número de conexiones, etc.

Desarrollo:

Actualmente, no hay forma de escalar dinámicamente un clúster de Redis cuando los requisitos de memoria de algunos servicios superan con creces las expectativas. Por lo tanto, brindamos soporte para el escalado dinámico. El cliente original usaba hash consistente para la política de enrutamiento de claves, pero esto era un poco complejo de implementar para el escalado dinámico, por lo que decidimos usar la fragmentación previa, que es relativamente simple de implementar. La ventaja del hash consistente es que es infinitamente escalable, mientras que el corte previo no lo es. Fragmentación previa

Especificaremos el número de ranuras en el clúster durante la fase de inicialización. Una vez que se especifica este número, no se puede cambiar. Este número de fragmentación previa es el número máximo de instancias de Redis que se pueden cambiar. ampliarse en el futuro. Suponiendo 128 sockets y 10G por instancia, entonces puede tener un clúster de terabytes, y si el clúster tiene un gran crecimiento de datos en el futuro, puede tener 1024 sockets, que básicamente pueden satisfacer todas sus necesidades de gran cantidad de memoria.

Nuestro clúster de Redis inicial tiene cuatro roles: cliente inteligente, redis, centinela y ZooKeeper. Para admitir la expansión dinámica, agregamos una función redis_cluster_manager (en lo sucesivo, administrador) para administrar el clúster de redis. Su trabajo principal es inicializar el clúster (es decir, antes de la fragmentación), modificar el estado de ZooKeeper después de agregar instancias y migrar datos a nuevas instancias cuando los clientes estén listos. Para minimizar el impacto en el rendimiento durante la migración de datos, solo migramos un segmento de datos a la vez y luego migramos el siguiente segmento de datos una vez completada la migración.

Como se muestra en la Figura 2

En comparación con el escenario original, la cantidad de ranuras, bloqueos de administrador, clientes y nodos de clientes migrados ha aumentado.

Slot: Todos los fragmentos escriben su información en los nodos de slot. Cuando el administrador inicializa el clúster, realizará una operación de fragmentación previa según la cantidad establecida de ranuras y la cantidad de grupos bajo el clúster, y asignará todas las ranuras de manera uniforme a los grupos existentes. La información del segmento consta de una cadena json, que registra el estado del segmento (stats), el grupo que actualmente posee el segmento (src) y el grupo al que se debe migrar (dst). Sharding tiene tres estados: en línea, previo a la migración y migración.

En línea significa que la fragmentación está en un estado normal. En este momento, dst está vacío y el cliente lee y escribe según el grupo src.

Pre_migrate (premigración) significa que el administrador marca el fragmento como que necesita migración. En este momento, dst todavía está vacío y el administrador está esperando que todos los clientes estén listos, porque ZooKeeper falla. volver a todos los clientes Hay un retraso de tiempo, por lo que si el administrador realiza la migración de datos cuando algunos clientes no están listos, habrá datos que deberán migrarse. Si algunos clientes no están listos cuando el administrador migra los datos, puede ocurrir una pérdida de datos.

La migración ocurre cuando el administrador confirma que todos los clientes están listos para la migración y luego escribe los datos en el grupo objetivo para ese fragmento en dst. Una vez completada la migración, el nombre del grupo objetivo se escribirá en src, dst se configurará en vacío y las estadísticas se configurarán en línea.

Bloqueo del administrador: dado que solo se permite migrar una ranura a la vez, no permitimos que varios administradores operen un clúster. Por lo tanto, antes de que un hipervisor opere un clúster, registrará un nodo temporal en "bloqueo de hipervisor" para indicar que el clúster ha sido operado por un hipervisor, de modo que cuando otros hipervisores quieran operar el clúster, saldrá automáticamente.

Clientes y Migración El cliente es el nodo que le permite al hipervisor saber si el cliente está listo. El cliente se representa a sí mismo con un uid en el formato client_language _ hostname _pid. Cuando el clúster no se migra, es decir, cuando todos los fragmentos están en línea, el cliente crea nodos temporales cuyo uid está en la máquina del cliente.

Cuando una ranura cambia del estado en línea al estado previo a la migración, el cliente elimina el nodo efímero uid en clientes y luego crea el nodo efímero uid en clientes migratorios. Cabe señalar que debido a la necesidad de garantizar que no se pierdan datos, esta ranura se bloqueará desde la etapa previa a la migración hasta la migración, es decir, se bloquearán todas las lecturas y escrituras en esta ranura. Por lo tanto, el administrador espera hasta 10 segundos para confirmar que todos los clientes han cambiado al estado listo y, si descubre que algún cliente no está listo, abandona la migración y cambia el estado de la ranura de premigración a en línea. Si el cliente descubre que el estado de la ranura ha cambiado de pre_migrate a online, eliminará el nodo uid en migring_clients y volverá a crear el nodo uid en clientes.

Otro problema a tener en cuenta es que es posible que el cliente haya iniciado y creado el nodo uid en el cliente, pero debido a retrasos en la red, el administrador aún no ha confirmado que el cliente esté listo, por lo que el administrador cambia el estado de la ranura de pre_migrate. Espere 1 segundo. después de conectarse para confirmar que todos los clientes estén listos.

Si el administrador descubre que no hay otros clientes bajo el cliente (están todos listos), cambia el estado de la ranura a Migrando. Cuando se migre la ranura, se liberará el bloqueo y el administrador recorrerá en iteración los datos del grupo src y migrará los datos de la ranura correspondiente al grupo dst. Si un cliente lee o escribe una clave para una ranura que se está migrando durante la migración, el cliente primero migra la clave del grupo src al grupo dst antes de leer o escribir la clave. Esto significa que el rendimiento del cliente se degradará durante este período. Esta es la razón por la que solo se migra una ranura a la vez. De esta manera, incluso para un clúster con solo 128 fragmentos, la cantidad de claves afectadas por el rendimiento durante la migración es solo 1/128, lo cual es aceptable.

El administrador descubre que la ranura ha sido migrada y escribe el nombre del grupo objetivo en src, configurando dst como vacío y stats en línea. El cliente también elimina el uid en migring_clients y crea un nodo uid en clientes.