Redis版本:Redis 4.0.1
 
Redis服务端有个入口,其入口如下: server.c -> main(int argc, char **argv)
 
服务端启动初始化流程分为如下5个步骤:
1 初始化服务器全局状态
-- 初始化 redisServer结构体对应的数据,如db数据库、事件状态、日志、AOF/RDB、统计信息
 
2 加载配置文件参数
-- 通过redis-server /etc/my-redis.conf加载my-redis.conf配置信息覆盖redis默认配置,比如port
 
3 初始化服务器功能模块
* 初始化 Redis 进程的信号功能。
* 初始化日志功能。
* 初始化客户端功能。
* 初始化共享对象。
* 初始化事件功能。
* 初始化网络连接。
* 初始化数据库。
* 初始化订阅与发布功能。
* 初始化各个统计变量。
* 关联服务器常规操作(cron job)到时间事件,关联客户端应答处理器到文件事件。
* 如果 AOF 功能已打开,那么打开或创建 AOF 文件。
* 设置内存限制。
* 初始化 Lua 脚本环境。
* 初始化慢查询功能。
* 初始化后台操作线程。
 
4 加载持久化数据   
根据配置模式的模式进行加载,如果开启aof就aof加载,否则rdb
 
5 开启事件循环,epoll事件循环
 
启动流程源码分析如下(具体见gitee: https://gitee.com/lidishan/redis-source-code-analysis/blob/master/src/server.c ):
int main( int argc, char **argv) {
    .........................省略,前面是一部分处理逻辑
    // 填充哨兵模式
    server.sentinel_mode = checkForSentinelMode(argc,argv);
     // 1 初始化服务器全局状态==========================================
    initServerConfig();
    // -- 初始化 ACL 控制相关
    ACLInit(); 
    // -- 初始化模块 加载动态链接库 可自定义命令,字典做映射关联
    moduleInitModulesSystem();
    tlsInit(); // 初始化 ssl
 
    // -- 设置传入的执行参数
    server.executable = getAbsolutePath(argv[ 0]);
    server.exec_argv = zmalloc( sizeof( char*)*(argc+ 1));
    server.exec_argv[argc] = NULL;
    for (j = 0; j < argc; j++) server.exec_argv[j] = zstrdup(argv[j]);
 
    if (server.sentinel_mode) {
        // 初始化哨兵相关配置
        initSentinelConfig(); // 初始化端口、保护模式
        initSentinel(); // 一些其他参数,有点多,自己看
    }
 
    // -- 判断是否需要启动 RDB AOF
    if (strstr(argv[ 0], "redis-check-rdb") != NULL)
        redis_check_rdb_main(argc,argv,NULL);
    else if (strstr(argv[ 0], "redis-check-aof") != NULL)
         redis_check_aof_main(argc,argv);
 
    if (argc >= 2) { // 如果命令行参数 >= 2
         j = 1; /* First option to parse in argv[] */
         sds options = sdsempty();
        // 判断第二个参数是否特殊参数,如果是就进行特殊处理,比如 -version 调用 version() 返回版本信息
        /* Handle special options --help and --version */
          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[ 1][ 0] != '-') {
            /* Replace the config file in server.exec_argv with its absolute path. */
             server.configfile = getAbsolutePath(argv[ 1]);
             zfree(server.exec_argv[ 1]);
             server.exec_argv[ 1] = zstrdup(server.configfile);
             j = 2; // Skip this arg when parsing options
         }
          while(j < argc) {
              /* Either first or last argument - Should we read config from stdin? */
              if (argv[j][ 0] == '-' && argv[j][ 1] == ' \0 ' && (j == 1 || j == argc-1)) {
              config_from_stdin = 1;
         }
        else if (argv[j][ 0] == '-' && argv[j][ 1] == '-') {
            /* Option name */
              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++;
    }
     // 2 加载配置文件参数,如./redis-server /path/to/redis.conf xxx
    loadServerConfig(server.configfile, config_from_stdin, options);
    if (server.sentinel_mode) loadSentinelConfigFromQueue();
         sdsfree(options);
    }
    // -- 如果是哨兵模式则检查配置文件参数
    if (server.sentinel_mode) sentinelCheckConfigFile();
    // -- 计算是否可以后台启动,可以则调用 daemonize()
    server.supervised = redisIsSupervised(server.supervised_mode);
    int background = server.daemonize && !server.supervised;
    if (background) daemonize(); // 设置了守护进程后,会把 pid 写入到 pidfile 指定的文件中
    // -- 写日志
    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/redis.conf", argv[ 0]);
    } else {
         serverLog(LL_WARNING, "Configuration loaded");
    }
    // 读取内存溢出评分的调整值
    readOOMScoreAdj();
     // 3 初始化服务器功能模块
    initServer();
    if (background || server.pidfile) createPidFile();
    // -- 将命令行下标为 0 的命令行参数设置为进程名
    if (server.set_proc_title) redisSetProcTitle(NULL);
    redisAsciiArt(); // 打印 redis 工作模式,端口进程号等
    checkTcpBacklogSettings();
 
    // -- 判断是否哨兵模式,然后打日志
    if (!server.sentinel_mode) {
        /* Things not needed when running in Sentinel mode. */
         serverLog(LL_WARNING, "Server initialized");
          #ifdef __linux__
             linuxMemoryWarnings();
          #if defined (__arm64__)
            int ret;
              if ((ret = linuxMadvFreeForkBugCheck())) {
                  if (ret == 1)
                        serverLog(LL_WARNING, "WARNING Your kernel has a bug that could lead to data corruption during background save. "
"Please upgrade to the latest stable kernel.");
                else
                        serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
"Your system could be affected, please report this error.");
                if (!checkIgnoreWarning( "ARM64-COW-BUG")) {
serverLog(LL_WARNING, "Redis will now exit to prevent data corruption. "
"Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG");
                        exit( 1);
                   }
              }
        #endif /* __arm64__ */
    #endif /* __linux__ */
    moduleInitModulesSystemLast();
    moduleLoadFromQueue();
    ACLLoadUsersAtStartup();
    InitServerLast();
     // 4 加载持久化数据 根据配置模式的模式进行加载,如果开启aofaof加载,否则rdb
    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 || server.tlsfd.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);     
    if (server.supervised_mode == SUPERVISED_SYSTEMD) {
        if (!server.masterhost) {
             redisCommunicateSystemd( "STATUS=Ready to accept connections \n ");
            } else {
                 redisCommunicateSystemd( "STATUS=Ready to accept connections in read-only mode. Waiting for MASTER <-> REPLICA sync \n ");     
            }
            redisCommunicateSystemd( "READY=1 \n ");
        }
    } else {
        // 到这步说明是哨兵模式,下面做一些哨兵的初始化收尾 和 判断哨兵是否运行中
         ACLLoadUsersAtStartup();
         InitServerLast();
         sentinelIsRunning();
        if (server.supervised_mode == SUPERVISED_SYSTEMD) {
              redisCommunicateSystemd( "STATUS=Ready to accept connections \n ");
              redisCommunicateSystemd( "READY=1 \n ");
         }
    }
 
    /* Warning the user about suspicious maxmemory setting. */
    if (server.maxmemory > 0 && server.maxmemory < 1024* 1024) { // 如果内存小于 1M ,输出警告日志
         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);
    }
    // -- 设置 CPU 亲缘属性
    redisSetCpuAffinity(server.server_cpulist);
    setOOMScoreAdj(- 1); // 设置内存溢出评分的调整值
    // 5 开启事件循环,epoll事件循环
    aeMain(server.el);
    // -- 服务器关闭,删除事件循环
    aeDeleteEventLoop(server.el);
    return 0;
}
 
 
 
 
 
 
 
 
        
 
 
 
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐