• 一文带你探究Sentinel的独特初始化


    摘要:本系列通过作者对Redis Sentinel源码的理解,详细说明Sentinel的代码实现方式。

    Redis Sentinel 是Redis提供的高可用模型解决方案。Sentinel可以自动监测一个或多个Redis主备实例,并在主实例宕机的情况下自动实行主备倒换。本系列通过作者对Redis Sentinel源码的理解,详细说明Sentinel的代码实现方式。

    Sentinel使用Redis内核相同的事件驱动代码框架, 但Sentinel有自己独特的初始化步骤。在这篇文章里,作者会介绍Sentinel与Redis服务器不同的初始化部分。

    我们可以通过redis-sentinel <path-to-configfile> 或者 redis-server <path-to-configfile> --sentinel 这两种方式启动并运行Sentinel实例,这两种方式是等价的。在Redis server.c 的main函数中,我们会看到Redis如何判断用户指定以Sentinel方式运行的逻辑:

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

    其中checkForSentinelMode函数会监测以下两种条件:

    1. 程序使用redis-sentinel可执行文件执行。

    2. 程序参数列表中有--sentinel 标志。

    以上任何一种条件成立则Redis会使用Sentinel的方式运行。

    /* Returns 1 if there is --sentinel among the arguments or if 
     
     * argv[0] contains "redis-sentinel". */ 
     
    int checkForSentinelMode(int argc, char **argv) { 
     
          int j; 
     
     
     
           if (strstr(argv[0],"redis-sentinel") != NULL) return 1; 
     
           for (j = 1; j < argc; j++) 
           if (!strcmp(argv[j],"--sentinel")) return 1; 
     
       return 0; 
     
    }

    在Redis 判断是否以Sentinel的方式运行以后,我们会看到如下代码段:

    int main(int argc, char **argv) { 
        struct timeval tv; 
        int j; 
     
        ............ 
    /* We need to init sentinel right now as parsing the configuration file 
        * in sentinel mode will have the effect of populating the sentinel 
        * data structures with master nodes to monitor. */ 
       if (server.sentinel_mode) { 
           initSentinelConfig(); 
           initSentinel(); 
     } 
     ............

    在initSentinelConfig函数中,会使用Sentinel特定的端口(默认为26379)来替代Redis的默认端口(6379)。另外,在Sentinel模式下,需要禁用服务器运行保护模式。

    /* This function overwrites a few normal Redis config default with Sentinel 
     * specific defaults. */ 
    void initSentinelConfig(void) { 
     server.port = REDIS_SENTINEL_PORT; 
     server.protected_mode = 0; /* Sentinel must be exposed. */ 
    }

    与此同时,initSentinel函数会做如下操作:

    /* Perform the Sentinel mode initialization. */ 
    void initSentinel(void) { 
     unsigned int j; 
     
     /* Remove usual Redis commands from the command table, then just add 
     * the SENTINEL command. */ 
     dictEmpty(server.commands,NULL); 
     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); 
     
     {"punsubscribe",punsubscribeCommand,-1,"",0,NULL,0,0,0,0,0}, 
     {"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0}, 
     {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0}, 
     {"role",sentinelRoleCommand,1,"ok-loading",0,NULL,0,0,0,0,0}, 
     {"client",clientCommand,-2,"read-only no-script",0,NULL,0,0,0,0,0}, 
     {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0}, 
     {"auth",authCommand,2,"no-auth no-script ok-loading ok-stale fast",0,NULL,0,0,0,0,0}, 
     {"hello",helloCommand,-2,"no-auth no-script fast",0,NULL,0,0,0,0,0} 
    };

    2.初始化Sentinel主状态结构,Sentinel主状态的定义及注释如下。

    /* Main state. */ 
     
    struct sentinelState { 
     
     char myid[CONFIG_RUN_ID_SIZE+1]; /* This sentinel ID. */ 
     
     uint64_t current_epoch; /* Current epoch. */ 
     
     dict *masters; /* Dictionary of master sentinelRedisInstances. 
     
     Key is the instance name, value is the 
     
     sentinelRedisInstance structure pointer. */ 
     
     int tilt; /* Are we in TILT mode? */ 
     
     int running_scripts; /* Number of scripts in execution right now. */ 
     
     mstime_t tilt_start_time; /* When TITL started. */ 
     
     mstime_t previous_time; /* Last time we ran the time handler. */ 
     
     list *scripts_queue; /* Queue of user scripts to execute. */ 
     
     char *announce_ip; /* IP addr that is gossiped to other sentinels if 
     
     not NULL. */ 
     
     int announce_port; /* Port that is gossiped to other sentinels if 
     
     non zero. */ 
    
      unsigned long simfailure_flags; /* Failures simulation. */ 
     
     int deny_scripts_reconfig; /* Allow SENTINEL SET ... to change script 
     
     paths at runtime? */ 
     
    } sentinel; 

    其中masters字典指针中的每个值都对应着一个Sentinel检测的主实例。

    在读取配置信息后,Redis服务器主函数会调用sentinelIsRunning函数, 做以下几个工作:

    1. 检查配置文件是否被设置,并且检查程序对配置文件是否有写权限,因为如果Sentinel状态改变的话,会不断将自己当前状态记录在配置文件中。

    2. 如果在配置文件中指定运行ID,Sentinel 会使用这个ID作为运行ID,相反地,如果没有指定运行ID,Sentinel会生成一个ID用来作为Sentinel的运行ID。

    3. 对所有的Sentinel监测实例产生初始监测事件。

    /* This function gets called when the server is in Sentinel mode, started, 
     * loaded the configuration, and is ready for normal operations. */ 
    void sentinelIsRunning(void) { 
     int j; 
     
     if (server.configfile == NULL) { 
     serverLog(LL_WARNING, 
     "Sentinel started without a config file. Exiting..."); 
     exit(1); 
     } else if (access(server.configfile,W_OK) == -1) { 
     serverLog(LL_WARNING, 
     "Sentinel config file %s is not writable: %s. Exiting...", 
     server.configfile,strerror(errno)); 
     exit(1); 
     } 
     
     /* If this Sentinel has yet no ID set in the configuration file, we 
     * pick a random one and persist the config on disk. From now on this 
     * will be this Sentinel ID across restarts. */ 
     for (j = 0; j < CONFIG_RUN_ID_SIZE; j++) 
     if (sentinel.myid[j] != 0) break; 
     
     if (j == CONFIG_RUN_ID_SIZE) { 
     /* Pick ID and persist the config. */ 
     getRandomHexChars(sentinel.myid,CONFIG_RUN_ID_SIZE); 
     sentinelFlushConfig(); 
     } 
     
    /* Log its ID to make debugging of issues simpler. */
     serverLog(LL_WARNING,"Sentinel ID is %s", sentinel.myid); 
     
     /* We want to generate a +monitor event for every configured master 
     * at startup. */ 
     sentinelGenerateInitialMonitorEvents(); 
    }

    参考资料:

    Redis设计与实现第二版 黄健宏著

    本文分享自华为云社区《Redis Sentinel 源码分析(1)Sentinel的初始化》,原文作者:中间件小哥 。

    点击关注,第一时间了解华为云新鲜技术~

  • 相关阅读:
    动态模板列更新数据分页的例子
    Oracle SCN机制解析
    阻止特定的ip登陆数据库的2种方法 (轉)
    Oracle动态执行SQL四种方式的例子
    使用Oracle的DBMS_SQL包执行动态SQL语句
    Oracle XML DB之浅入浅出
    将oracle设为归档模式和非归档模式启动的方法
    如何修改Oracle數據庫字符集
    (原創)C#使用QueryTables導出到Excel
    常用SQL語句2
  • 原文地址:https://www.cnblogs.com/huaweiyun/p/14281056.html
Copyright © 2020-2023  润新知