Redis Server启动过程_redis-server 启动

     * is parsed, if any. */
    while(j != argc) {
        if (argv[j][0] == '-' && argv[j][1] == '-') {
            /* Option name */
            if (!strcmp(argv[j], "--check-rdb")) {
                /* Argument has no options, need to skip for parsing. */
                j++;
                continue;
            }
            if (sdslen(options)) options = sdscat(options,"\n");
            options = sdscat(options,argv[j]+2);
            options = sdscat(options," ");
        } else {
            /* Option argument */
            options = sdscatrepr(options,argv[j],strlen(argv[j]));
            options = sdscat(options," ");
        }
        j++;
    }
    if (server.sentinel_mode && configfile && *configfile == '-') {
        serverLog(LL_WARNING,
            "Sentinel config from STDIN not allowed.");
        serverLog(LL_WARNING,
            "Sentinel needs config file on disk to save state.  Exiting...");
        exit(1);
    }
    resetServerSaveParams();
    loadServerConfig(configfile,options);
    sdsfree(options);
}

serverLog(LL_WARNING, "oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo");
serverLog(LL_WARNING,
    "Redis version=%s, bits=%d, commit=%s, modified=%d, pid=%d, just started",
        REDIS_VERSION,
        (sizeof(long) == 8) ? 64 : 32,
        redisGitSHA1(),
        strtol(redisGitDirty(),NULL,10) > 0,
        (int)getpid());

if (argc == 1) {
    serverLog(LL_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/%s.conf", argv[0], server.sentinel_mode ? "sentinel" : "redis");
} else {
    serverLog(LL_WARNING, "Configuration loaded");
}

server.supervised = redisIsSupervised(server.supervised_mode);
int background = server.daemonize && !server.supervised;
if (background) daemonize();

initServer();
if (background || server.pidfile) createPidFile();
redisSetProcTitle(argv[0]);
redisAsciiArt();
checkTcpBacklogSettings();

if (!server.sentinel_mode) {
    /* Things not needed when running in Sentinel mode. */
    serverLog(LL_WARNING,"Server initialized");
#ifdef __linux__
    linuxMemoryWarnings();
#endif
    moduleLoadFromQueue();
    InitServerLast();
    loadDataFromDisk();
    if (server.cluster_enabled) {
        if (verifyClusterConfigWithData() == C_ERR) {
            serverLog(LL_WARNING,
                "You can't have keys in a DB different than DB 0 when in "
                "Cluster mode. Exiting.");
            exit(1);
        }
    }
    if (server.ipfd_count > 0)
        serverLog(LL_NOTICE,"Ready to accept connections");
    if (server.sofd > 0)
        serverLog(LL_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
} else {
    InitServerLast();
    sentinelIsRunning();
}

/* Warning the user about suspicious maxmemory setting. */
if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {
    serverLog(LL_WARNING,"WARNING: You specified a maxmemory value that is less than 1MB (current value is %llu bytes). Are you sure this is what you really want?", server.maxmemory);
}

aeSetBeforeSleepProc(server.el,beforeSleep);
aeSetAfterSleepProc(server.el,afterSleep);
aeMain(server.el);
aeDeleteEventLoop(server.el);
return 0;

}


main函数中执行部分包含下面几个阶段:


1. 基本初始化阶段
2. 检查配置阶段
3. 运行参数解析阶段
4. 初始化server阶段
5. 执行事件驱动框架阶段


### 基本初始化阶段


main 函数开始有一段宏定义覆盖的代码,这部分如何定义了REDIS\_TEST宏定义,并且Redis server启动时的参数符合测试参数,那么main函数会执行响应的测试程序,比如启动参数argv的第二个参数等于test,且argv的第三个参数等于ziplist那么就执行ziplistTest测试函数。



#ifdef REDIS_TEST
if (argc == 3 && !strcasecmp(argv[1], “test”)) {
if (!strcasecmp(argv[2], “ziplist”)) {
return ziplistTest(argc, argv);
} else if (!strcasecmp(argv[2], “quicklist”)) {
quicklistTest(argc, argv);
} else if (!strcasecmp(argv[2], “intset”)) {
return intsetTest(argc, argv);
} else if (!strcasecmp(argv[2], “zipmap”)) {
return zipmapTest(argc, argv);
} else if (!strcasecmp(argv[2], “sha1test”)) {
return sha1Test(argc, argv);
} else if (!strcasecmp(argv[2], “util”)) {
return utilTest(argc, argv);
} else if (!strcasecmp(argv[2], “endianconv”)) {
return endianconvTest(argc, argv);
} else if (!strcasecmp(argv[2], “crc64”)) {
return crc64Test(argc, argv);
} else if (!strcasecmp(argv[2], “zmalloc”)) {
return zmalloc_test(argc, argv);
}

    return -1; /* test not found */
}

#endif


后面主要设置server运行的地域、时区信息,设置内存分配异常处理句柄,设置哈希函数的随机种子等,该部分代码如下,



// 设置地域信息
setlocale(LC_COLLATE,"");
// 设置时区
tzset(); /* Populates 'timezone' global. */
// 设置内存分配异常处理句柄
zmalloc_set_oom_handler(redisOutOfMemoryHandler);
// 生成随机数种子
srand(time(NULL)^getpid());
// 获取当前时间
gettimeofday(&tv,NULL); 
// 设置随机种子
char hashseed[16];
// 设置哈希因子
getRandomHexChars(hashseed,sizeof(hashseed));
dictSetHashFunctionSeed((uint8_t*)hashseed);

### 检查配置阶段


该阶段检查哨兵模式,并检查是否要执行RDB检测或AOF检测。Redis server启动后需要根据Redis配置的参数检查是否设置了哨兵模式,如果有则会调用initSentinelConfig函数,对哨兵模式的参数进行初始化设置,以及调用initSentinel函数,代码如下,



// 判断server是否设置为哨兵模式,如果是则进行哨兵模式初始化
if (server.sentinel_mode) {
    initSentinelConfig();
    initSentinel();
}

接着检查是否要执行RDB检测或AOF检测,如果运行的是redis-check-rdb程序,调用redis\_check\_rdb\_main函数检测RDB文件,如果运行的是redis-check-aof程序,调用redis\_check\_aof\_main函数检测AOF文件,对应代码如下,



// 如果运行的是redis-check-rdb程序,调用redis_check_rdb_main函数检测RDB文件
if (strstr(argv[0],"redis-check-rdb") != NULL)
    redis_check_rdb_main(argc,argv,NULL);
// 如果运行的是redis-check-aof程序,调用redis_check_aof_main函数检测AOF文件
else if (strstr(argv[0],"redis-check-aof") != NULL)
    redis_check_aof_main(argc,argv);
    ... ...
}

### 运行参数解析阶段


该阶段解析运行参数,根据运行参数检查内存大小,获取绝对路径,并且调用loadServerConfig函数,并对命令行参数和配置文件中的参数进行设置,然后为Redis各功能模块的关键参数设置合适的值,以便Redis高效运行。



if (argc >= 2) {
    j = 1; /* First option to parse in argv[] */
    sds options = sdsempty();
    char *configfile = NULL;
    if (strcmp(argv[1], "-v") == 0 ||
        strcmp(argv[1], "--version") == 0) version();
    if (strcmp(argv[1], "--help") == 0 ||
        strcmp(argv[1], "-h") == 0) usage();
    if (strcmp(argv[1], "--test-memory") == 0) {
        if (argc == 3) {
            memtest(atoi(argv[2]),50);
            exit(0);
        } else {
            fprintf(stderr,"Please specify the amount of memory to test in megabytes.\n");
            fprintf(stderr,"Example: ./redis-server --test-memory 4096\n\n");
            exit(1);
        }
    }
    if (argv[j][0] != '-' || argv[j][1] != '-') {
        configfile = argv[j];
        server.configfile = getAbsolutePath(configfile);
        /* Replace the config file in server.exec_argv with
         * its absolute path. */
        zfree(server.exec_argv[j]);
        server.exec_argv[j] = zstrdup(server.configfile);
        j++;
    }
    while(j != argc) {
        if (argv[j][0] == '-' && argv[j][1] == '-') {
            /* Option name */
            if (!strcmp(argv[j], "--check-rdb")) {
                /* Argument has no options, need to skip for parsing. */
                j++;
                continue;
            }
            if (sdslen(options)) options = sdscat(options,"\n");
            options = sdscat(options,argv[j]+2);
            options = sdscat(options," ");
        } else {
            /* Option argument */
            options = sdscatrepr(options,argv[j],strlen(argv[j]));
            options = sdscat(options," ");
        }
        j++;
    }
    if (server.sentinel_mode && configfile && *configfile == '-') {
        serverLog(LL_WARNING,
            "Sentinel config from STDIN not allowed.");
        serverLog(LL_WARNING,
            "Sentinel needs config file on disk to save state.  Exiting...");
        exit(1);
    }
    resetServerSaveParams();
    loadServerConfig(configfile,options);
    sdsfree(options);
}

### 初始化server阶段


完成参数解析和设置后,会打印服务器的一些日志信息,后面再调用initServer函数,对server运行时的各种资源进行初始化工作。主要包括server资源管理所需的数据结构初始化、键值对数据库初始化、server网络框架初始化等。而在调用完initServer函数后,main函数还会判断server是否为哨兵模式。如果是则会设置启动哨兵模式,否则则调用loadDataFromDisk函数,从磁盘上加载AOF或RDB文件,以便恢复之前的数据。



initServer();
if (background || server.pidfile) createPidFile();
redisSetProcTitle(argv[0]);
redisAsciiArt();
checkTcpBacklogSettings();

if (!server.sentinel_mode) {
    /* Things not needed when running in Sentinel mode. */
    serverLog(LL_WARNING,"Server initialized");
#ifdef __linux__
    linuxMemoryWarnings();
#endif
    moduleLoadFromQueue();
    InitServerLast();
    loadDataFromDisk();
    if (server.cluster_enabled) {
        if (verifyClusterConfigWithData() == C_ERR) {
            serverLog(LL_WARNING,
                "You can't have keys in a DB different than DB 0 when in "
                "Cluster mode. Exiting.");
            exit(1);
        }
    }
    if (server.ipfd_count > 0)
        serverLog(LL_NOTICE,"Ready to accept connections");
    if (server.sofd > 0)
        serverLog(LL_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
} else {
    InitServerLast();
    sentinelIsRunning();
}

### 执行事件驱动框架阶段


为了能高效处理高并发的客户端连接请求,Redis采用了事件驱动框架来处理不同客户端的连接和读写请求。main 函数最后会调用aeMain函数进行事件驱动框架,开始循环处理各种触发的事件。代码片段如下,



int main(int argc, char **argv) {
… …
aeSetBeforeSleepProc(server.el,beforeSleep);
aeSetAfterSleepProc(server.el,afterSleep);
aeMain(server.el);
aeDeleteEventLoop(server.el);
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值