Redis源码解析:21sentinel(二)定期发送消息、检测主观下线

Redis哨兵系统通过定时发送"PING"、"INFO"和"PUBLISH"命令,监控实例状态。"PING"用于探测实例存活,"INFO"获取实例信息,"PUBLISH"在HELLO频道发布哨兵和主节点信息。哨兵通过对比收到的INFO回复和HELLO消息,更新实例和哨兵状态,判断主观下线。当超过预设时间未收到"PING"响应或实例角色变化,实例可能被视为主观下线。
摘要由CSDN通过智能技术生成

六:定时发送消息

         哨兵每隔一段时间,会向其所监控的所有实例发送一些命令,用于获取这些实例的状态。这些命令包括:”PING”、”INFO”和”PUBLISH”。

         “PING”命令,主要用于哨兵探测实例是否活着。如果对方超过一段时间,还没有回复”PING”命令,则认为其是主观下线了。

         “INFO”命令,主要用于哨兵获取实例当前的状态和信息,比如该实例当前是主节点还是从节点;该实例反馈的IP地址和PORT信息,是否与我记录的一样;该实例如果是主节点的话,那它都有哪些从节点;该实例如果是从节点的话,它与主节点是否连通,它的优先级是多少,它的复制偏移量是多少等等,这些信息在故障转移流程中,是判断实例状态的重要信息;

         “PUBLISH”命令,主要用于哨兵向实例的HELLO频道发布有关自己以及主节点的信息,也就是所谓的HELLO消息。因为所有哨兵都会订阅主节点和从节点的HELLO频道,因此,每个哨兵都会收到其他哨兵发布的信息。

         因此,通过这些命令,尽管在配置文件中只配置了主节点的信息,但是哨兵可以通过主节点的”INFO”回复,得到所有从节点的信息;又可以通过订阅实例的HELLO频道,接收其他哨兵通过”PUBLISH”命令发布的信息,从而得到监控同一主节点的所有其他哨兵的信息。

 

         在“主函数”sentinelHandleRedisInstance中,是通过调用sentinelSendPeriodicCommands来发送这些命令的。注意,以上的命令都有自己的发送周期,在sentinelSendPeriodicCommands函数中,并不是一并发送三个命令,而是发送那些,按照发送周期应该发送的命令。该函数的代码如下:

void sentinelSendPeriodicCommands(sentinelRedisInstance *ri) {
    mstime_t now = mstime();
    mstime_t info_period, ping_period;
    int retval;

    /* Return ASAP if we have already a PING or INFO already pending, or
     * in the case the instance is not properly connected. */
    if (ri->flags & SRI_DISCONNECTED) return;

    /* For INFO, PING, PUBLISH that are not critical commands to send we
     * also have a limit of SENTINEL_MAX_PENDING_COMMANDS. We don't
     * want to use a lot of memory just because a link is not working
     * properly (note that anyway there is a redundant protection about this,
     * that is, the link will be disconnected and reconnected if a long
     * timeout condition is detected. */
    if (ri->pending_commands >= SENTINEL_MAX_PENDING_COMMANDS) return;

    /* If this is a slave of a master in O_DOWN condition we start sending
     * it INFO every second, instead of the usual SENTINEL_INFO_PERIOD
     * period. In this state we want to closely monitor slaves in case they
     * are turned into masters by another Sentinel, or by the sysadmin. */
    if ((ri->flags & SRI_SLAVE) &&
        (ri->master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS))) {
        info_period = 1000;
    } else {
        info_period = SENTINEL_INFO_PERIOD;
    }

    /* We ping instances every time the last received pong is older than
     * the configured 'down-after-milliseconds' time, but every second
     * anyway if 'down-after-milliseconds' is greater than 1 second. */
    ping_period = ri->down_after_period;
    if (ping_period > SENTINEL_PING_PERIOD) ping_period = SENTINEL_PING_PERIOD;

    if ((ri->flags & SRI_SENTINEL) == 0 &&
        (ri->info_refresh == 0 ||
        (now - ri->info_refresh) > info_period))
    {
        /* Send INFO to masters and slaves, not sentinels. */
        retval = redisAsyncCommand(ri->cc,
            sentinelInfoReplyCallback, NULL, "INFO");
        if (retval == REDIS_OK) ri->pending_commands++;
    } else if ((now - ri->last_pong_time) > ping_period) {
        /* Send PING to all the three kinds of instances. */
        sentinelSendPing(ri);
    } else if ((now - ri->last_pub_time) > SENTINEL_PUBLISH_PERIOD) {
        /* PUBLISH hello messages to all the three kinds of instances. */
        sentinelSendHello(ri);
    }
}

         如果实例标志位中设置了SRI_DISCONNECTED标记,说明当前实例的异步上下文还没有创建好,因此直接返回;

         实例的pending_commands属性,表示已经向该实例发送的命令中,尚有pending_commands个命令还没有收到回复。每次调用redisAsyncCommand函数,向实例异步发送一条命令之后,就会增加该属性的值,而每当收到命令回复之后,就会减少该属性的值;

         因此,如果该属性的值大于SENTINEL_MAX_PENDING_COMMANDS(100),说明该实例尚有超过100条命令的回复信息没有收到。这种情况下,说明与实例的连接已经不正常了,为了节约内存,因此直接返回;

         接下来计算info_period和ping_period,这俩值表示发送"INFO"和"PING"命令的时间周期。如果当前时间距离上次收到"INFO"或"PING"回复的时间已经超过了info_period或ping_period,则向实例发送"INFO"或"PING"命令;

         如果当前实例为从节点,并且该从节点对应的主节点已经客观下线了,则置info_period为1000,否则的话置为SENTINEL_INFO_PERIOD(10000)。之所以在主节点客观下线后更频繁的向从节点发送"INFO"命令,是因为从节点可能会被置为新的主节点,因此需要更加实时的获取其状态;

         将ping_period置为ri->down_after_period的值,该属性的值是根据配置文件中down-after-milliseconds选项得到的,如果该属性值大于SENTINEL_PING_PERIOD(1000),则将ping_period置为SENTINEL_PING_PERIOD;

         接下来开始发送命令:如果当前实例不是哨兵实例,并且距离上次收到"INFO"命令回复已经超过了info_period,则向该实例异步发送"INFO"命令。

         否则,如果距离上次收到"PING"命令回复已经超过了ping_period,则调用函数sentinelSendPing向该实例异步发送"PING"命令;

         否则,如果距离上次收到"PUBLISH"命令的回复已经超过了SENTINEL_PUBLISH_PERIOD(2000),则调用函数sentinelSendHello向该实例异步发送"PUBLISH"命令;

         因此,"PING"用于探测实例是否活着,可以发送给所有类型的实例;而"INFO"命令用于获取实例的信息,只需发送给主节点和从节点实例;而"PUBLISH"用于向HELLO频道发布哨兵本身和主节点的信息,除了发送给主节点和从节点之外,哨兵本身也实现了"PUBLISH"命令的处理函数,因此该命令也会发送给哨兵实例。

 

1:PING消息

         函数sentinelSendPing用于向实例发送”PING”命令,因为该命令用于探测实例是否主观下线,因此等到后面讲解主观下线是在分析。

 

2:HELLO消息

         函数sentinelSendHello用于发布HELLO消息,它的代码如下:

int sentinelSendHello(sentinelRedisInstance *ri) {
    char ip[REDIS_IP_STR_LEN];
    char payload[REDIS_IP_STR_LEN+1024];
    int retval;
    char *announce_ip;
    int announce_port;
    sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ? ri : ri->master;
    sentinelAddr *master_addr = sentinelGetCurrentMasterAddress(master);

    if (ri->flags & SRI_DISCONNECTED) return REDIS_ERR;

    /* Use the specified announce address if specified, otherwise try to
     * obtain our own IP address. */
    if (sentinel.announce_ip) {
        announce_ip = sentinel.announce_ip;
    } else {
        if (anetSockName(ri->cc->c.fd,ip,sizeof(ip),NULL) == -1)
            return REDIS_ERR;
        announce_ip = ip;
    }
    announce_port = sentinel.announce_port ?
                    sentinel.announce_port : server.port;

    /* Format and send the Hello message. */
    snprintf(payload,sizeof(payload),
        "%s,%d,%s,%llu," /* Info about this sentinel. */
        "%s,%s,%d,%llu", /* Info about current master. */
        announce_ip, announce_port, server.runid,
        (unsigned long long) sentinel.curren
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值