目录
- 1、 初始化参数配置
- 2、 加载并解析配置文件
- 3、 初始化服务器内部变量
- 4、执行事件驱动框架
本文基于社区版redis 4.0.8
1、 初始化参数配置
由函数initserverconfig()实现,具体操作就是给配置参数赋初始化值:
//设置时区
setlocale(lc_collate,"");
//设置随机种子
char hashseed[16];
getrandomhexchars(hashseed,sizeof(hashseed));
dictsethashfunctionseed((uint8_t*)hashseed);
//初始化module
void initserverconfig(void) {
//servercron函数执行频率,默认10ms
server.hz = config_default_hz;
//监听端口,默认6379
server.port = config_default_server_port;
server.tcp_backlog = config_default_tcp_backlog;
server.dbnum = config_default_dbnum;
......
//初始化命令表
server.commands = dictcreate(&commandtabledicttype,null);
server.orig_commands = dictcreate(&commandtabledicttype,null);
populatecommandtable();
server.delcommand = lookupcommandbycstring("del");
server.multicommand = lookupcommandbycstring("multi");
server.lpushcommand = lookupcommandbycstring("lpush");
server.lpopcommand = lookupcommandbycstring("lpop");
server.rpopcommand = lookupcommandbycstring("rpop");
server.sremcommand = lookupcommandbycstring("srem");
server.execcommand = lookupcommandbycstring("exec");
server.expirecommand = lookupcommandbycstring("expire");
server.pexpirecommand = lookupcommandbycstring("pexpire");
......
}
2、 加载并解析配置文件
在这一阶段,会对命令行传入的参数进行解析,并且调用 loadserverconfig 函数,对命令行参数和配置文件中的参数进行合并处理,然后为 redis 各功能模块的关键参数设置合适的取值,以便 server 能高效地运行。
//filename表示配置文件全路径名称;
//options表示命令行输入的配置参数,如port=4000
void loadserverconfig(char *filename, char *options) {
sds config = sdsempty();
char buf[config_max_line+1];
/* 加载配置文件到内存 */
if (filename) {
file *fp;
if (filename[0] == '-' && filename[1] == '//filename表示配置文件全路径名称;
//options表示命令行输入的配置参数,如port=4000
void loadserverconfig(char *filename, char *options) {
sds config = sdsempty();
char buf[config_max_line+1];
/* 加载配置文件到内存 */
if (filename) {
file *fp;
if (filename[0] == '-' && filename[1] == '\0') {
fp = stdin;
} else {
if ((fp = fopen(filename,"r")) == null) {
serverlog(ll_warning,
"fatal error, can't open config file '%s'", filename);
exit(1);
}
}
while(fgets(buf,config_max_line+1,fp) != null)
config = sdscat(config,buf);
if (fp != stdin) fclose(fp);
}
/* append the additional options */
if (options) {
config = sdscat(config,"\n");
config = sdscat(config,options);
}
//解析配置
loadserverconfigfromstring(config);
sdsfree(config);
}') {
fp = stdin;
} else {
if ((fp = fopen(filename,"r")) == null) {
serverlog(ll_warning,
"fatal error, can't open config file '%s'", filename);
exit(1);
}
}
while(fgets(buf,config_max_line+1,fp) != null)
config = sdscat(config,buf);
if (fp != stdin) fclose(fp);
}
/* append the additional options */
if (options) {
config = sdscat(config,"\n");
config = sdscat(config,options);
}
//解析配置
loadserverconfigfromstring(config);
sdsfree(config);
}
3、 初始化服务器内部变量
在完成对运行参数的解析和设置后,main 函数会调用 initserver 函数,对 server 运行时的各种资源进行初始化工作。包括了 server 资源管理所需的数据结构初始化、键值对数据库初始化、server 网络框架初始化等。
最后调用 loaddatafromdisk 函数,从磁盘上加载 aof 或者是 rdb 文件,以便恢复之前的数据。
void initserver(void) {
/* 初始化需要的各种资源 */
server.clients = listcreate();//初始化客户端链表
server.pid = getpid();
server.current_client = null;
server.clients = listcreate();
server.clients_to_close = listcreate();
server.slaves = listcreate();
server.monitors = listcreate();
server.clients_pending_write = listcreate();
server.slaveseldb = -1; /* force to emit the first select command. */
server.unblocked_clients = listcreate();
server.ready_keys = listcreate();
server.clients_waiting_acks = listcreate();
server.get_ack_from_slaves = 0;
server.clients_paused = 0;
server.system_memory_size = zmalloc_get_memory_size();
createsharedobjects();
//调用aecreateeventloop函数创建aeeventloop结构体,并赋值给server结构的el变量
//maxclients 变量的值大小,可以在 redis 的配置文件 redis.conf 中进行定义,默认值是 1000
server.el = aecreateeventloop(server.maxclients+config_fdset_incr);
if (server.el == null) {
serverlog(ll_warning,
"failed creating the event loop. error message: '%s'",
strerror(errno));
exit(1);
}
......
/* 创建数据库结构*/
for (j = 0; j < server.dbnum; j++) {
server.db[j].dict = dictcreate(&dbdicttype,null);
server.db[j].expires = dictcreate(&keyptrdicttype,null);
server.db[j].blocking_keys = dictcreate(&keylistdicttype,null);
server.db[j].ready_keys = dictcreate(&objectkeypointervaluedicttype,null);
server.db[j].watched_keys = dictcreate(&keylistdicttype,null);
server.db[j].id = j;
server.db[j].avg_ttl = 0;
}
......
//创建事件循环框架
server.el = aecreateeventloop(server.maxclients+config_fdset_incr);
…
//开始监听设置的网络端口
if (server.port != 0 &&
listentoport(server.port,server.ipfd,&server.ipfd_count) == c_err)
exit(1);
…
//为server后台任务创建定时事件
if (aecreatetimeevent(server.el, 1, servercron, null, null) == ae_err) {
serverpanic("can't create event loop timers.");
exit(1);
}
…
//为每一个监听的ip设置连接事件的处理函数accepttcphandler
for (j = 0; j < server.ipfd_count; j++) {
if (aecreatefileevent(server.el, server.ipfd[j], ae_readable,
accepttcphandler,null) == ae_err)
{ … }
}
}
4、执行事件驱动框架
事件驱动框架是 redis server 运行的核心。该框架一旦启动后,就会一直循环执行,每次循环会处理一批触发的网络读写事件。main 函数直接调用事件框架的主体函数 aemain(在ae.c文件中)后,就进入事件处理循环了。当然,在进入事件驱动循环前,main 函数会分别调用 aesetbeforesleepproc 和 aesetaftersleepproc 两个函数,来设置每次进入事件循环前 server 需要执行的操作,以及每次事件循环结束后 server 需要执行的操作。下面代码显示了这部分的执行逻辑,你可以看下。
aesetbeforesleepproc(server.el,beforesleep); aesetaftersleepproc(server.el,aftersleep); aemain(server.el); aedeleteeventloop(server.el);
到此这篇关于redis server启动过程的详细步骤的文章就介绍到这了,更多相关redis server启动过程 内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!