Redis Sentinel

En el artículo anterior, presentamos la tecnología de replicación de sincronización de datos entre los nodos maestro y esclavo redis. Mediante una copia completa y una propagación ininterrumpida de comandos, podemos lograr el efecto de la copia de seguridad sincrónica de los datos maestros y esclavos. Una vez que el nodo maestro está inactivo, podemos elegir uno El esclavo que trabaja se convierte en el nuevo nodo maestro y permite que otros esclavos lo sincronicen.

Esta es una forma de lidiar con la conmutación por error de redis, pero no tiene practicidad de producción, porque después de todo, es manual para tratar la falla, y el nodo de tiempo de falla de redis es impredecible. Necesitamos un componente de monitoreo automático que nos ayude a manejar automáticamente la conmutación por error.

El modo Redis Sentinel (Sentinel) es una "cosa" que monitorea y maneja automáticamente la transferencia de nodos defectuosos entre Redis. Sentinel es en realidad un programa de servidor Redis, pero se ejecuta en un modo especial y no proporciona almacenamiento de datos. Servicios, solo para monitoreo y gestión de nodos redis ordinarios.

1. ¿Qué es Sentinel?

Sentinel es en realidad un programa de servidor redis. También ejecuta la función serverCron regularmente, pero no es utilizada por otros programas. Utiliza los módulos de monitoreo y conmutación por error de los nodos redis normales.

Cuando se inicializa Sentinel, borrará la tabla de comandos original y escribirá sus propios comandos únicos, por lo que los comandos de lectura y escritura de datos compatibles con los nodos redis normales no se encuentran para Sentinel, porque no inicializa estos comandos en absoluto. Actuador

Sentinel ejecutará periódicamente el comando de información en el maestro que supervisa para obtener la última relación maestro-esclavo. También enviará periódicamente comandos de detección de latidos de ping a todos los nodos redis. Si se detecta que un maestro no puede responder, se enviará a otro Sentinel envía un mensaje, subjetivamente cree que el maestro está inactivo, si el clúster de Sentinel acepta que el número de maestros sin conexión alcanza un valor, entonces todos están de acuerdo y desconectan el maestro.

Lo que debe hacer antes de desconectarse es encontrar uno de los grupos de Sentinel para realizar la operación fuera de línea. Este paso se llama elección de líder. Después de la elección, se seleccionará un maestro adecuado de todos los nodos esclavos del maestro, y dejar Otros esclavos vuelven a sincronizar el nuevo maestro.

De hecho, lo anterior hemos presentado brevemente qué es Sentinel y qué se ha hecho en esencia. Elaboraremos los detalles de la implementación en combinación con el código fuente. Aquí veremos cómo configurar e iniciar una supervisión de Sentinel. (Se recomienda configurar más de tres en el entorno de producción)

El primer paso es iniciar un nodo de servidor redis ordinario:

imagen

No hay nada que decir en este paso, comenzamos en un puerto predeterminado 6379.

El segundo paso es iniciar tres nodos esclavos diferentes:

imagen

El tercer paso es escribir el archivo de configuración centinela:

imagen

Expliquemos el significado de estas configuraciones. Dijimos que Sentinel es en realidad un servidor redis que se ejecuta en un modo especial, por lo que necesita ejecutar un puerto. A continuación, configuramos el nodo maestro redis para que sea monitoreado por el centinela actual y el parámetro objetivo fuera de línea a través del comando centinela monitor mymaster . Sentinel down-after-miliseconds configura un parámetro. .

La configuración de sincronización paralela centinela se utiliza para limitar el número de nodos esclavos que pueden sincronizar la mayoría de los datos después de la conmutación maestro-esclavo, porque sabemos que el maestro-esclavo realiza la fase de sincronización completa, y el nodo esclavo no proporciona servicios al cargar datos. Entonces, cuanto más corto sea el tiempo para completar el interruptor maestro-esclavo, por supuesto, también conducirá a un gran número de nodos esclavos que no pueden proporcionar servicios de lectura, y viceversa.

Sentinel failover-timeout configura el tiempo de espera máximo para la conmutación por error .

El cuarto paso es iniciar Sentinel:

Use el comando redis-sentinel [config] para iniciar tres centinelas.

imagen

En este caso, de hecho, hemos completado una configuración simple de clúster centinela, apaguemos manualmente el maestro y veamos si el centinela completo realiza la conmutación por error por nosotros.

imagen

imagen

A partir de los resultados, sentinel establece automáticamente el nodo esclavo original 7003 como el nuevo maestro para nosotros. No elaboraremos el proceso específico. Lo presentaremos en detalle junto con el código fuente. Aquí deberíamos tener una comprensión general de la aplicación real de centinela. .

2. Cómo funciona Sentinel

Cuando usamos el comando redis-sentinel para iniciar centinela,

int main(int argc, char **argv) {
    。。。。。
    server.sentinel_mode = checkForSentinelMode(argc,argv);
    。。。。。
    if (server.sentinel_mode) {
        initSentinelConfig();
        initSentinel();
    }
    。。。。。
}

La función checkForSentinelMode verificará y juzgará si se inicia en modo centinela de acuerdo con su comando y parámetros, si es así, devolverá 1 y viceversa. Si lo inicia centinela, se realizará una inicialización centinela.

void initSentinelConfig(void) {
    server.port = REDIS_SENTINEL_PORT; //26379
}

initSentinelConfig es en realidad para inicializar el puerto actual de ejecución centinela, el valor predeterminado es 26379.

void initSentinel(void) {
    unsigned int j;
    //清空普通redis-server下可用的命令表
    dictEmpty(server.commands,NULL);
    //加载sentinel需要的命令
    for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) {
        int retval;
        struct redisCommand *cmd = sentinelcmds+j;

        retval = dictAdd(server.commands, sdsnew(cmd->name), cmd);
        serverAssert(retval == DICT_OK);
    }

    sentinel.current_epoch = 0;
    //根据配置文件,初始化自己需要监控的master(一个sentinel是可能监控多个 master的)
    sentinel.masters = dictCreate(&instancesDictType,NULL);
    sentinel.tilt = 0;
    sentinel.tilt_start_time = 0;
    sentinel.previous_time = mstime();
    sentinel.running_scripts = 0;
    sentinel.scripts_queue = listCreate();
    sentinel.announce_ip = NULL;
    sentinel.announce_port = 0;
    sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE;
    sentinel.deny_scripts_reconfig = SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG;
    memset(sentinel.myid,0,sizeof(sentinel.myid));
}

La función principal de initSentinel es borrar la tabla de comandos de redis en modo normal, cargar comandos exclusivos de sentinel e inicializar la colección maestra que supervisa.

En este punto, incluso si se completa la inicialización de centinela, el monitoreo automático restante está en la función de temporización serverCron, echemos un vistazo.

    //间隔 100 毫秒执行一次 sentinelTimer
    run_with_period(100) {
        if (server.sentinel_mode) sentinelTimer();
    }

Es decir, después de que se inicia sentinel, la función sentinelTimer se llamará una vez en serverCron para procesar algunos eventos importantes a intervalos de 100 milisegundos (de hecho, sentinelTimer modificará el intervalo de ejecución).

void sentinelTimer(void) {
    sentinelCheckTiltCondition();
    sentinelHandleDictOfRedisInstances(sentinel.masters);
    sentinelRunPendingScripts();
    sentinelCollectTerminatedScripts();
    sentinelKillTimedoutScripts();
    server.hz = CONFIG_DEFAULT_HZ + rand() % CONFIG_DEFAULT_HZ;
}

El cuerpo de SentinelTimer es muy corto, pero no seas muy feliz. No entremos en la función sentinelCheckTiltCondition. Redis depende en gran medida de la hora del sistema. Si se detecta repetidamente la época del reloj del sistema, determinará que el sistema actual es inestable y entra en TITL. Es similar a un estado de reposo y no nos conmutará por error. Simplemente recopile datos y espere a que el sistema se estabilice.

void sentinelHandleDictOfRedisInstances(dict *instances) {
    dictIterator *di;
    dictEntry *de;
    sentinelRedisInstance *switch_to_promoted = NULL;
    di = dictGetIterator(instances);
    //递归遍历监控的所有 master,执行监控操作
    while((de = dictNext(di)) != NULL) {
        sentinelRedisInstance *ri = dictGetVal(de);
        //这是监控的核心逻辑,下文细说
        sentinelHandleRedisInstance(ri);
        if (ri->flags & SRI_MASTER) {
            //不论是 slave 还是其他 sentinel,都视作一个redisInstance
            sentinelHandleDictOfRedisInstances(ri->slaves);
            sentinelHandleDictOfRedisInstances(ri->sentinels);
            if (ri->failover_state == SENTINEL_FAILOVER_STATE_UPDATE_CONFIG) {
                switch_to_promoted = ri;
            }
        }
    }
    if (switch_to_promoted)
        sentinelFailoverSwitchToPromotedSlave(switch_to_promoted);
    dictReleaseIterator(di);
}

sentinelHandleRedisInstance se compone principalmente de dos partes, monitoreo y conmutación por error.

void sentinelHandleRedisInstance(sentinelRedisInstance *ri) {
    sentinelReconnectInstance(ri);
    sentinelSendPeriodicCommands(ri);

    if (sentinel.tilt) {
        if (mstime()-sentinel.tilt_start_time < SENTINEL_TILT_PERIOD) return;
        sentinel.tilt = 0;
        sentinelEvent(LL_WARNING,"-tilt",NULL,"#tilt mode exited");
    }

    sentinelCheckSubjectivelyDown(ri);

    if (ri->flags & (SRI_MASTER|SRI_SLAVE)) {
        /* Nothing so far. */
    }

    if (ri->flags & SRI_MASTER) {
        sentinelCheckObjectivelyDown(ri);
        if (sentinelStartFailoverIfNeeded(ri))
            sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_ASK_FORCED);
        sentinelFailoverStateMachine(ri);
        sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_NO_FLAGS);
    }
}

La función sentinelReconnectInstance hace dos cosas: debido a que actualmente es una instancia centinela, lo primero es establecer una conexión con la instancia actualmente atravesada, ya sea maestra, esclava o centinela, y enviar un comando ping después de que la conexión se haya establecido con éxito. En segundo lugar, si el recorrido actual es maestro o esclavo, se suscribirá a su canal sentinel_hello y, cuando haya una actualización de mensaje en este canal, transmitirá a todos los clientes suscritos del canal. (La función principal de suscribirse a este canal todavía se usa para descubrir otros centinelas e intercambiar sus puntos de vista sobre nodos monitoreados con otros centinelas)

La función sentinelSendPeriodicCommands envía comandos de información al maestro y al esclavo cada 10 segundos de forma predeterminada para comprender su relación maestro-esclavo. Si esta instancia está subjetivamente fuera de línea, la frecuencia de envío de comandos de información se acelerará para garantizar que conozca la relación maestro-esclavo lo antes posible. Los cambios también harán ping a todos los tipos de instancias cada segundo.

Lo anterior es en realidad parte del nodo de monitoreo en sentinelHandleRedisInstance, a continuación continuamos viendo cómo hacer su failover.

void sentinelHandleRedisInstance(sentinelRedisInstance *ri) {
    sentinelReconnectInstance(ri);
    sentinelSendPeriodicCommands(ri);

    //判断是否需要进入 tilt 模式
    if (sentinel.tilt) {
        if (mstime()-sentinel.tilt_start_time < SENTINEL_TILT_PERIOD) return;
        sentinel.tilt = 0;
        sentinelEvent(LL_WARNING,"-tilt",NULL,"#tilt mode exited");
    }
    //判断是否需要主观下线该节点
    sentinelCheckSubjectivelyDown(ri);

    if (ri->flags & (SRI_MASTER|SRI_SLAVE)) {
        /* Nothing so far. */
    }

    if (ri->flags & SRI_MASTER) {
        //判断是否需要客户下线该节点
        sentinelCheckObjectivelyDown(ri);
        //如果确定该节点客观下线,进行领导者选举
        if (sentinelStartFailoverIfNeeded(ri))
            sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_ASK_FORCED);
            //故障转移
        sentinelFailoverStateMachine(ri);
        sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_NO_FLAGS);
    }
}

sentinelCheckSubjectivelyDown detecta si el nodo actual necesita estar subjetivamente fuera de línea, y la condición de juicio es que el nodo tiene su propia configuración. Si la instancia actual no responde a su propio ping más allá del período de tiempo configurado, entonces se considera que está fuera de línea y establece el indicador subjetivo fuera de línea.

sentinelCheckObjectivelyDown Detecta si se alcanza la condición actual del objetivo fuera de línea. La lógica de detección es esta. Itere a través de todas las estructuras centinela hermanas para ver si tienen subjetivamente fuera de línea el nodo actual. Cuente el número. Si alcanza el quórum, el maestro está objetivamente fuera de línea , Establezca la bandera y notifique a otros hermanos centinela a través del canal.

sentinelStartFailoverIfNeeded determina si actualmente hay una conmutación por error centinela (un bit indicador del maestro, si hay una conmutación por error centinela, se establecerá este bit indicador), si existe, no participe, no haga nada.

sentinelAskMasterStateToOtherSentinels enviará mensajes a otros centinelas, pidiéndole que acepte la conmutación por error del maestro como líder. Cómo hacerlo específicamente, en primer lugar, obtendré la información sobre todos los hermanos centinela de mi lado para realizar un recorrido, y les enviaré el comando is-master-down-by-addr para pedirles que acepten convertirse en líderes y establecer la función de devolución de llamada sentinelReceiveIsMasterDownReply Manejar la respuesta.

Si un centinela recibe el voto de un líder de otra persona y no ha votado por otros, estará de acuerdo, de lo contrario será ignorado.

Cuando un centinela recibe suficientes votos, se considera el líder, marca al maestro como conmutación por error y realiza una verdadera operación de conmutación por error.

void sentinelFailoverStateMachine(sentinelRedisInstance *ri) {
    serverAssert(ri->flags & SRI_MASTER);

    if (!(ri->flags & SRI_FAILOVER_IN_PROGRESS)) return;

    switch(ri->failover_state) {
        //故障转移开始
        case SENTINEL_FAILOVER_STATE_WAIT_START:
            sentinelFailoverWaitStart(ri);
            break;
        //选择一个要晋升的从节点
        case SENTINEL_FAILOVER_STATE_SELECT_SLAVE:
            sentinelFailoverSelectSlave(ri);
            break;
        //发送slaveof no one命令,使从节点变为主节点
        case SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE:
            sentinelFailoverSendSlaveOfNoOne(ri);
            break;
        //等待被选择的从节点晋升为主节点,如果超时则重新选择晋升的从节点
        case SENTINEL_FAILOVER_STATE_WAIT_PROMOTION:
            sentinelFailoverWaitPromotion(ri);
            break;
        //给所有的从节点发送slaveof命令,同步新的主节点
        case SENTINEL_FAILOVER_STATE_RECONF_SLAVES:
            sentinelFailoverReconfNextSlave(ri);
            break;
    }
}

La conmutación por error sentinelFailoverStateMachine consta de cinco pasos, que se dividen en cinco sentinelTimer para realizar el procesamiento periódico. Cuando se complete la elección del nuevo maestro, se transmitirá a otros hermanos centinela para informarles que el nuevo maestro ha aparecido. Después de que lo reciban, cancelarán la desconexión subjetiva del maestro original y reiniciarán la supervisión del nuevo maestro.

Hasta ahora, nuestra introducción y análisis del código fuente de Sentinel ha terminado. Es esencialmente un servidor redisfuncionamiento que se ejecuta en un modo especial. Al hacer ping constantemente a los nodos maestro y esclavo, después de detectar que pueden estar defectuosos, colectivamente realizamos un voto para determinar y Elegir a una persona para ejecutar la línea descendente objetiva del maestro.

En el siguiente artículo, veremos el clúster más poderoso en redis.


Presta atención al público sin perderte, un programador al que le encanta compartir.
¡La cuenta pública responde "1024" más el autor WeChat para discutir y aprender juntos!
Todos los materiales de código de caso utilizados en cada artículo se cargarán en mi github personal
https://github.com/SingleYam/overview_java
Bienvenido a pisar!

Cuenta pública YangAM

Supongo que te gusta

Origin www.cnblogs.com/yangming1996/p/12677771.html
Recomendado
Clasificación