Redis源码剖析和注释(二十三)--- Redis Sentinel实现(哨兵的执行过程和执行的内容)

Redis Sentinel实现(上)

1. Redis Sentinel 介绍和部署

请参考Redis Sentinel 介绍与部署

sentinel.c文件详细注释:Redis Sentinel详细注释

本文会分为两篇分别接受Redis Sentinel的实现,本篇主要将Redis哨兵的执行过程和执行的内容。

标题4将会在Redis Sentinel实现(下)中详细剖析。

2. Redis Sentinel 的执行过程和初始化

Sentinel本质上是一个运行在特殊模式下的Redis服务器,无论如何,都是执行服务器的main来启动。主函数中关于Sentinel启动的代码如下:

int main(int argc, char **argv) {
    // 1. 检查开启哨兵模式的两种方式
    server.sentinel_mode = checkForSentinelMode(argc,argv);
    // 2. 如果已开启哨兵模式,初始化哨兵的配置
    if (server.sentinel_mode) {
        initSentinelConfig();
        initSentinel();
    }
    // 3. 载入配置文件
    loadServerConfig(configfile,options);
    // 开启哨兵模式,哨兵模式和集群模式只能开启一种
    if (!server.sentinel_mode) {
        // 在不是哨兵模式下,会载入AOF文件和RDB文件,打印内存警告,集群模式载入数据等等操作。
    } else { 
        sentinelIsRunning();
    }
}

以上过程可以分为四步:

  • 检查是否开启哨兵模式
  • 初始化哨兵的配置
  • 载入配置文件
  • 开启哨兵模式

2.1 检查是否开启哨兵模式

Redis Sentinel 介绍与部署文章中,介绍了两种开启的方法:

  • redis-sentinel sentinel.conf
  • redis-server sentinel.conf --sentinel

主函数中调用了checkForSentinelMode()函数来判断是否开启哨兵模式。

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;
}

如果开启了哨兵模式,就会将server.sentinel_mode设置为1

2.2 初始化哨兵的配置

在主函数中调用了两个函数initSentinelConfig()initSentinel(),前者用来初始化Sentinel节点的默认配置,后者用来初始化Sentinel节点的状态。sentinel.c文件详细注释:Redis Sentinel详细注释

sentinel.c文件中定义了一个全局变量sentinel,它是struct sentinelState类型的,用于保存当前Sentinel的状态。

  • initSentinelConfig(),初始化哨兵节点的默认端口为26379。
// 设置Sentinel的默认端口,覆盖服务器的默认属性
void initSentinelConfig(void) {
    server.port = REDIS_SENTINEL_PORT;
}
  • initSentinel(),初始化哨兵节点的状态
// 执行Sentinel模式的初始化操作
void initSentinel(void) {
    unsigned int j;

    /* Remove usual Redis commands from the command table, then just add
     * the SENTINEL command. */
    // 将服务器的命令表清空
    dictEmpty(server.commands,NULL);
    // 只添加Sentinel模式的相关命令,Sentinel模式下一共11个命令
    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);
    }

    /* Initialize various data structures. */
    // 初始化各种Sentinel状态的数据结构

    // 当前纪元,用于实现故障转移操作
    sentinel.current_epoch = 0;
    // 监控的主节点信息的字典
    sentinel.masters = dictCreate(&instancesDictType,NULL);
    // TILT模式
    sentinel.tilt = 0;
    sentinel.tilt_start_time = 0;
    // 最后执行时间处理程序的时间
    sentinel.previous_time = mstime();
    // 正在执行的脚本数量
    sentinel.running_scripts = 0;
    // 用户脚本的队列
    sentinel.scripts_queue = listCreate();
    // Sentinel通过流言协议接收关于主服务器的ip和port
    sentinel.announce_ip = NULL;
    sentinel.announce_port = 0;
    // 故障模拟
    sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE;
    // Sentinel的ID置为0
    memset(sentinel.myid,0,sizeof(sentinel.myid));
}

在哨兵模式下,只有11条命令可以使用,因此要用哨兵模式的命令表来代替Redis原来的命令表。

之后就是初始化sentinel的成员变量。我们重点关注这几个成员:

  • dict *masters :当前哨兵节点监控的主节点字典。字典的键是主节点实例的名字,字典的值是一个指针,指向一个sentinelRedisInstance类型的结构。
  • int running_scripts: 当前正在执行的脚本的数量。
  • list *scripts_queue:保存要执行用户脚本的队列。

2.3 载入配置文件

在启动哨兵节点时,要指定一个.conf配置文件,配置文件可以将配置项分为两类。

Sentinel配置说明

  • sentinel monitor \ \ \ \
    • 例如:sentinel monitor mymaster 127.0.0.1 6379 2
    • 当前Sentinel节点监控 127.0.0.1:6379 这个主节点
    • 2 代表判断主节点失败至少需要2个Sentinel节点节点同意
    • mymaster 是主节点的别名
  • sentinel xxxxxx \ xxxxxx
    • 例如:sentinel down-after-milliseconds mymaster 30000
    • 每个Sentinel节点都要定期PING命令来判断Redis数据节点和其余Sentinel节点是否可达,如果超过30000毫秒且没有回复,则判定不可达。
    • 例如:sentinel parallel-syncs mymaster 1
    • 当Sentinel节点集合对主节点故障判定达成一致时,Sentinel领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,限制每次向新的主节点发起复制操作的从节点个数为1。

配置文件以这样的格式告诉哨兵节点,监控的主节点是谁,有什么样的限制条件。如果想要监控多个主节点,只需按照此格式在配置文件中多写几份。

既然配置文件都是如此,那么处理的函数也是如此处理,由于配置项很多,但是大体相似,所以我们列举处理示例的代码块:

    sentinelRedisInstance *ri;

    // SENTINEL monitor选项
    if (!strcasecmp(argv[0],"monitor") && argc == 5) {
        /* monitor <name> <host> <port> <quorum> */
        int quorum = atoi(argv[4]); //获取投票数
        // 投票数必须大于等于1
        if (quorum <= 0) return "Quorum must be 1 or greater.";
        // 创建一个主节点实例,并加入到Sentinel所监控的master字典中
        if (createSentinelRedisInstance(argv[1],SRI_MASTER,argv[2],
     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值