【源码】RedisServer启动过程

博客 动态
0 234
羽尘
羽尘 2022-02-07 20:55:03
悬赏:0 积分 收藏

【源码】Redis Server启动过程

本文基于社区版Redis 4.0.8
 
 
 

1、 初始化参数配置

由函数initServerConfig()实现,具体操作就是给配置参数赋初始化值:
//设置时区setlocale(LC_COLLATE,"");//设置随机种子char hashseed[16];getRandomHexChars(hashseed,sizeof(hashseed));dictSetHashFunctionSeed((uint8_t*)hashseed);//初始化modulevoid 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=4000void 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);}

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

posted @ 2022-02-07 20:22 赵海亮的博客 阅读(10) 评论(0) 编辑 收藏 举报
回帖
    羽尘

    羽尘 (王者 段位)

    2335 积分 (2)粉丝 (11)源码

     

    温馨提示

    亦奇源码

    最新会员