Redis Sentinel实现(上)
1. Redis Sentinel 介绍和部署
请参考
sentinel.c文件详细注释:
本文会分为两篇分别接受Redis Sentinel的实现,本篇主要将Redis哨兵的执行过程和执行的内容。
标题4将会在中详细剖析。
2. Redis Sentinel 的执行过程和初始化
Sentinel本质上是一个运行在特殊模式下的Redis服务器,无论如何,都是执行服务器的main来启动。主函数中关于Sentinel启动的代码如下:
int main(int argc, char **argv) {
server.sentinel_mode = checkForSentinelMode(argc,argv);
if (server.sentinel_mode) {
initSentinelConfig();
initSentinel();
}
loadServerConfig(configfile,options);
if (!server.sentinel_mode) {
} else {
sentinelIsRunning();
}
}
以上过程可以分为四步:
- 检查是否开启哨兵模式
- 初始化哨兵的配置
- 载入配置文件
- 开启哨兵模式
2.1 检查是否开启哨兵模式
在文章中,介绍了两种开启的方法:
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文件详细注释:
在sentinel.c文件中定义了一个全局变量sentinel,它是struct sentinelState类型的,用于保存当前Sentinel的状态。
initSentinelConfig(),初始化哨兵节点的默认端口为26379。
void initSentinelConfig(void) {
server.port = REDIS_SENTINEL_PORT;
}
initSentinel(),初始化哨兵节点的状态
void initSentinel(void) {
unsigned int j;
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);
}
sentinel.current_epoch = 0;
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;
memset(sentinel.myid,0,sizeof(sentinel.myid));
}
在哨兵模式下,只有11条命令可以使用,因此要用哨兵模式的命令表来代替Redis原来的命令表。
之后就是初始化sentinel的成员变量。我们重点关注这几个成员:
- dict *masters :当前哨兵节点监控的主节点字典。字典的键是主节点实例的名字,字典的值是一个指针,指向一个
sentinelRedisInstance类型的结构。
- int running_scripts: 当前正在执行的脚本的数量。
- list *scripts_queue:保存要执行用户脚本的队列。
2.3 载入配置文件
在启动哨兵节点时,要指定一个.conf配置文件,配置文件可以将配置项分为两类。
- 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;
if (!strcasecmp(argv[0],"monitor") && argc == 5) {
int quorum = atoi(argv[4]);
if (quorum <= 0) return "Quorum must be 1 or greater.";
if (createSentinelRedisInstance(argv[1],SRI_MASTER,argv[2],