关于redis sentinel的启动
******************************************************************************
1启动方式
可以看到在server.c中,下面这一行判断是否是sentinel模式启动
server.sentinel_mode = checkForSentinelMode(argc,argv);
******************************************************************************
这个函数具体如下
/* Returns 1 if there is --sentinel among the arguments or if
* argv[0] contains "redis-sentinel". */
如果在参数中间存在--sentinel或者第一个启动参数是redis-sentinel,那么就返回1,即是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 sentinel.conf
或者
redis-server sentinel.conf --sentinel
当然你可以采用下面这种多余的模式
redis-sentinel sentinel.conf --sentinel
******************************************************************************
2.初始化
可以看到在server.c中,存在如下的初始化sentinel的代码
/* 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. */
现在我们需要开始初始化sentinel,用sentinel的模式解析配置文件中的参数,
这样可以用主机节点的信息填充sentinel的数据结构,从而达到监督的目的
if (server.sentinel_mode) {
initSentinelConfig();
initSentinel();
}
这两个函数的具体逻辑如下
******************************************************************************
函数initSentinelConfig
/* This function overwrites a few normal Redis config default with Sentinel
* specific defaults. */
这个函数用sentinel特殊的默认值重写一小部分正常的redis服务的默认配置
void initSentinelConfig(void) {
server.port = REDIS_SENTINEL_PORT; 这里是端口号 #define REDIS_SENTINEL_PORT 26379
server.protected_mode = 0; /* Sentinel must be exposed. */ sentinel必须能被访问,要不然起不到监督的作用
}
******************************************************************************
函数initSentinel
/* Perform the Sentinel mode initialization. */ 执行sentinel模式的初始化
void initSentinel(void) {
unsigned int j;
/* Remove usual Redis commands from the command table, then just add
* the SENTINEL command. */
将常用的redis命令从命令表中移除,将sentinel的命令加入到表中
dictEmpty(server.commands,NULL); 移除原来redis命令
for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) { 循环添加sentinel命令
int retval;
struct redisCommand *cmd = sentinelcmds+j;
retval = dictAdd(server.commands, sdsnew(cmd->name), cmd); 添加新命令
serverAssert(retval == DICT_OK);
/* Translate the command string flags description into an actual
* set of flags. */
将命令字符串标志描述转换为实际的标志集合
if (populateCommandTableParseFlags(cmd,cmd->sflags) == C_ERR)
serverPanic("Unsupported command flag");
}
/* Initialize various data structures. */ 初始化各种不同的数据结构
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;
sentinel.deny_scripts_reconfig = SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG;
memset(sentinel.myid,0,sizeof(sentinel.myid));
}
常用的sentinel命令,命令都对应具体的处理函数
struct redisCommand sentinelcmds[] = {
{"ping",pingCommand,1,"",0,NULL,0,0,0,0,0},
{"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0},
{"subscribe",subscribeCommand,-2,"",0,NULL,0,0,0,0,0},
{"unsubscribe",unsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},
{"psubscribe",psubscribeCommand,-2,"",0,NULL,0,0,0,0,0},
{"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}
};
***************************************************************************************
在主函数的定时任务函数serverCron中
存在Sentinel的定时任务, 如下:
/* Run the Sentinel timer if we are in sentinel mode. */
if (server.sentinel_mode) sentinelTimer();
***************************************************************************************
这个任务是redis服务初始化的时候注册的时间事件,具体的代码如下
void initServer(void) {
。。。。。。。
/* Create the timer callback, this is our way to process many background
* operations incrementally, like clients timeout, eviction of unaccessed
* expired keys and so forth. */
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
注册serverCron时间事件,这是我们注册的唯一一个时间事件
serverPanic("Can't create event loop timers.");
exit(1);
}
。。。。。。。
}
***************************************************************************************
正常情况下,通过AE.C的代码,我们可以看到
函数aeCreateTimeEvent创建一个时间事件的时候,
long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
aeTimeProc *proc, void *clientData,
aeEventFinalizerProc *finalizerProc)
{
。。。。。。
aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms); 这里的milliseconds就是触发下次事件间隔时间
。。。。。。
}
***************************************************************************************
具体的触发处理调用过程如下
/* Process time events */
static int processTimeEvents(aeEventLoop *eventLoop) {
。。。。。。
aeGetTime(&now_sec, &now_ms);
if (now_sec > te->when_sec ||
(now_sec == te->when_sec && now_ms >= te->when_ms))
{
int retval;
id = te->id;
te->refcount++;
retval = te->timeProc(eventLoop, id, te->clientData); 这里的处理函数就是我们之前注册的serverCron
te->refcount--;
processed++;
if (retval != AE_NOMORE) {
如果不是一次性的时间,需要循环触发的,这里需要再次加上下次触发的间隔时间
这里的retval是处理函数的返回值,所以需要查看serverCron的返回值,可以看到函数serverCron的返回值
为 return 1000/server.hz;可以看到初始值为 server.hz = CONFIG_DEFAULT_HZ; #define CONFIG_DEFAULT_HZ 10
因此每个默认情况下,每隔100毫秒触发一次
aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
} else {
te->id = AE_DELETED_EVENT_ID;
}
}
。。。。。。。
}
接下来我们的sentinel定时器默认每隔100毫秒会调用一次,开始正常sentinel的正常工作
***************************************************************************************