数据库
首页 > 数据库> > Redis集群分析(35)

Redis集群分析(35)

作者:互联网

1、故障转移

在(34)中提到了failover_state的状态会被设置为:SENTINEL_FAILOVER_STATE_UPDATE_CONFIG。这里需要注意的是这个状态的处理方法并不在之前提到的sentinelFailoverStateMachine中。这个方法的处理五个状态中并不包含上述状态。除了上述状态外还有一个状态:SENTINEL_FAILOVER_STATE_NONE。

整个故障转移流程的状态如下所示:

/* Failover machine different states. */
#define SENTINEL_FAILOVER_STATE_NONE 0  /* No failover in progress. */
#define SENTINEL_FAILOVER_STATE_WAIT_START 1  /* Wait for failover_start_time*/
#define SENTINEL_FAILOVER_STATE_SELECT_SLAVE 2 /* Select slave to promote */
#define SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE 3 /* Slave -> Master */
#define SENTINEL_FAILOVER_STATE_WAIT_PROMOTION 4 /* Wait slave to change role */
#define SENTINEL_FAILOVER_STATE_RECONF_SLAVES 5 /* SLAVEOF newmaster */
#define SENTINEL_FAILOVER_STATE_UPDATE_CONFIG 6 /* Monitor promoted slave. */

上述一共有7个状态,其中值为0的状态(即SENTINEL_FAILOVER_STATE_NONE)代表没有故障转移的状态。而1到5为之前解析的sentinelFailoverStateMachine方法中的状态。最后值为6的状态为文档(34)中提到的状态。

对于整个故障转移来说需要将该状态重新设置为0,并将相关参数设置为没有故障转移的状态,这个过程才算完成。所以即使在(34)的解析中Redis服务器已经能够正常提供服务,这个故障转移流程也没有结束。

而对于状态SENTINEL_FAILOVER_STATE_UPDATE_CONFIG的处理方法为:sentinelHandleDictOfRedisInstances。这个方法在文档(26)中提到过。它是有哨兵的sentinelTimer方法调用,而在其方法内部会调用一个重要的函数:sentinelHandleRedisInstance。sentinelHandleDictOfRedisInstances方法的具体内容如下:

/* Perform scheduled operations for all the instances in the dictionary.
 * Recursively call the function against dictionaries of slaves. */
void sentinelHandleDictOfRedisInstances(dict *instances) {
    dictIterator *di;
    dictEntry *de;
    sentinelRedisInstance *switch_to_promoted = NULL;

    /* There are a number of things we need to perform against every master. */
    di = dictGetIterator(instances);
    while((de = dictNext(di)) != NULL) {
        sentinelRedisInstance *ri = dictGetVal(de);

        sentinelHandleRedisInstance(ri);
        if (ri->flags & SRI_MASTER) {
            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);
}

在代码第17行出现了我们要分析的状态SENTINEL_FAILOVER_STATE_UPDATE_CONFIG,这里的逻辑很简单:先检查状态是否为需求状态,若是则为参数switch_to_promoted赋值,然后在第22行的if语句内的内容会被执行。这里它只执行了一个方法:sentinelFailoverSwitchToPromotedSlave。
该方法内容如下:

/* This function is called when the slave is in
 * SENTINEL_FAILOVER_STATE_UPDATE_CONFIG state. In this state we need
 * to remove it from the master table and add the promoted slave instead. */
void sentinelFailoverSwitchToPromotedSlave(sentinelRedisInstance *master) {
    sentinelRedisInstance *ref = master->promoted_slave ?
                                 master->promoted_slave : master;

    sentinelEvent(LL_WARNING,"+switch-master",master,"%s %s %d %s %d",
        master->name, master->addr->ip, master->addr->port,
        ref->addr->ip, ref->addr->port);

    sentinelResetMasterAndChangeAddress(master,ref->addr->ip,ref->addr->port);
}

这里首先看第5行,这里在为参数ref赋值,而赋值的内容是master的promoted_slave参数,这个参数在处理SELECT_SLAVE状态的操作中出现过。在处理该状态的时候会调用一个名为sentinelFailoverSelectSlave的方法,这个方法会在从服务器中选择一台新的服务器作为主服务器,而选中的服务器会被记录到参数promoted_slave中。

然后便是第12行的sentinelResetMasterAndChangeAddress方法,这个方法内容如下:

/* Reset the specified master with sentinelResetMaster(), and also change
 * the ip:port address, but take the name of the instance unmodified.
 *
 * This is used to handle the +switch-master event.
 *
 * The function returns C_ERR if the address can't be resolved for some
 * reason. Otherwise C_OK is returned.  */
int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *ip, int port) {
    sentinelAddr *oldaddr, *newaddr;
    sentinelAddr **slaves = NULL;
    int numslaves = 0, j;
    dictIterator *di;
    dictEntry *de;

    newaddr = createSentinelAddr(ip,port);
    if (newaddr == NULL) return C_ERR;

    /* Make a list of slaves to add back after the reset.
     * Don't include the one having the address we are switching to. */
    di = dictGetIterator(master->slaves);
    while((de = dictNext(di)) != NULL) {
        sentinelRedisInstance *slave = dictGetVal(de);

        if (sentinelAddrIsEqual(slave->addr,newaddr)) continue;
        slaves = zrealloc(slaves,sizeof(sentinelAddr*)*(numslaves+1));
        slaves[numslaves++] = createSentinelAddr(slave->addr->ip,
                                                 slave->addr->port);
    }
    dictReleaseIterator(di);

    /* If we are switching to a different address, include the old address
     * as a slave as well, so that we'll be able to sense / reconfigure
     * the old master. */
    if (!sentinelAddrIsEqual(newaddr,master->addr)) {
        slaves = zrealloc(slaves,sizeof(sentinelAddr*)*(numslaves+1));
        slaves[numslaves++] = createSentinelAddr(master->addr->ip,
                                                 master->addr->port);
    }

    /* Reset and switch address. */
    sentinelResetMaster(master,SENTINEL_RESET_NO_SENTINELS);
    oldaddr = master->addr;
    master->addr = newaddr;
    master->o_down_since_time = 0;
    master->s_down_since_time = 0;

    /* Add slaves back. */
    for (j = 0; j < numslaves; j++) {
        sentinelRedisInstance *slave;

        slave = createSentinelRedisInstance(NULL,SRI_SLAVE,slaves[j]->ip,
                    slaves[j]->port, master->quorum, master);
        releaseSentinelAddr(slaves[j]);
        if (slave) sentinelEvent(LL_NOTICE,"+slave",slave,"%@");
    }
    zfree(slaves);

    /* Release the old address at the end so we are safe even if the function
     * gets the master->addr->ip and master->addr->port as arguments. */
    releaseSentinelAddr(oldaddr);
    sentinelFlushConfig();
    return C_OK;
}

这个方法主要为了清空原master的数据并写入新master的数据。首先是第15行这里会创建新master的ip与port信息。然后是第20行到29行将原master的所有的从服务器(除新master外)添加到一个临时参数slaves中去。然后是第34行到38行,将原master也添加到slaves中去。然后是第41行调用了一个sentinelResetMaster方法,这个方法的内容如下:

/* Reset the state of a monitored master:
 * 1) Remove all slaves.
 * 2) Remove all sentinels.
 * 3) Remove most of the flags resulting from runtime operations.
 * 4) Reset timers to their default value. For example after a reset it will be
 *    possible to failover again the same master ASAP, without waiting the
 *    failover timeout delay.
 * 5) In the process of doing this undo the failover if in progress.
 * 6) Disconnect the connections with the master (will reconnect automatically).
 */

#define SENTINEL_RESET_NO_SENTINELS (1<<0)
void sentinelResetMaster(sentinelRedisInstance *ri, int flags) {
    serverAssert(ri->flags & SRI_MASTER);
    dictRelease(ri->slaves);
    ri->slaves = dictCreate(&instancesDictType,NULL);
    if (!(flags & SENTINEL_RESET_NO_SENTINELS)) {
        dictRelease(ri->sentinels);
        ri->sentinels = dictCreate(&instancesDictType,NULL);
    }
    instanceLinkCloseConnection(ri->link,ri->link->cc);
    instanceLinkCloseConnection(ri->link,ri->link->pc);
    ri->flags &= SRI_MASTER;
    if (ri->leader) {
        sdsfree(ri->leader);
        ri->leader = NULL;
    }
    ri->failover_state = SENTINEL_FAILOVER_STATE_NONE;
    ri->failover_state_change_time = 0;
    ri->failover_start_time = 0; /* We can failover again ASAP. */
    ri->promoted_slave = NULL;
    sdsfree(ri->runid);
    sdsfree(ri->slave_master_host);
    ri->runid = NULL;
    ri->slave_master_host = NULL;
    ri->link->act_ping_time = mstime();
    ri->link->last_ping_time = 0;
    ri->link->last_avail_time = mstime();
    ri->link->last_pong_time = mstime();
    ri->role_reported_time = mstime();
    ri->role_reported = SRI_MASTER;
    if (flags & SENTINEL_GENERATE_EVENT)
        sentinelEvent(LL_WARNING,"+reset-master",ri,"%@");
}

如同其注释所述,这个方法会重置master,其中主要有6个步骤:1、删除记录的所有的从服务器;2、删除记录的所有的哨兵;3、删除运行时操作产生的大多数标志;4、将计时器重置为其默认值;5、撤销故障转移;6、关闭连接。

细看代码主要是对一些参数的赋值操作,其中大多数参数都在之前的文档中提到过。如用于存储从服务器的slaves,存储哨兵服务器的sentinels,标识故障转移状态的failover_state等等。

然后继续回到sentinelResetMasterAndChangeAddress方法,在执行完sentinelResetMaster方法后,接着第43行对服务器地址重新赋值,第44行、45行重置主客观状态。
然后是第48行到56行,将先前设置到临时参数slaves中的数据添加到新master的slaves参数中。
最后是第61行刷新配置文件。

标签:slave,addr,Redis,35,集群,slaves,SENTINEL,master,ri
来源: https://blog.csdn.net/qq_39210987/article/details/113916537