启动过程大体可以分为5个阶段

  • 基本初始化

    • 设置server运行时区
    • 设置hash函数的随机种子
      ...
      // 设置时区
      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 检查

      ...
      // 检查是否是哨兵模式
      server.sentinel_mode = checkForSentinelMode(argc,argv);
      // 初始化server的配置
      initServerConfig();
      moduleInitModulesSystem();
      ...
      // 如果是哨兵模式,会再次进行哨兵模式的初始化
      if (server.sentinel_mode) {
          initSentinelConfig();
          initSentinel();
      }
    
      // 检查是否要执行 RDB 检测或 AOF 检查
      /* Check if we need to start in redis-check-rdb/aof mode. We just execute
       * the program main. However the program is part of the Redis executable
       * so that we can easily execute an RDB check on loading errors.
       **/
      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);
  • 运行参数解析

      ...
      // 参数为两个时,进行一些特殊处理
      /* 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);
          }
      }
    
      /* First argument is the config file name? */
      // 比如说这样启动:redis-server ./redis.conf
      // ./redis.conf 其实就是配置文件的路径
      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++;
      }
      // 继续解析命令行参数
      ...
      // 启动中打印日志
      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());
      // 手动启动redis时,我们经常会看到的一些日志
      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");
      }
      
      // redis-server是否后台运行, 支持的模式, 从配置中,我们可以看到redis支持的有以下几种
      # If you run Redis from upstart or systemd, Redis can interact with your
      # supervision tree. Options:
      #   supervised no      - no supervision interaction
      #   supervised upstart - signal upstart by putting Redis into SIGSTOP mode
      #   supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
      #   supervised auto    - detect upstart or systemd method based on
      #                        UPSTART_JOB or NOTIFY_SOCKET environment variables
      # Note: these supervision methods only signal "process is ready."
      #       They do not enable continuous liveness pings back to your supervisor.
      // 但是需要注意的是,server.daemonize和server.supervised看起来是无法同时满足的
      server.supervised = redisIsSupervised(server.supervised_mode);
      int background = server.daemonize && !server.supervised;
      if (background) daemonize();

    至此,redis的配置文件加载就完毕了,接下来开始初始化server

  • 初始化server

      initServer();
      ...
      这段代码设置了两个信号的处理方式。
      首先,signal(SIGHUP, SIG_IGN) 表示忽略 SIGHUP 信号。SIGHUP 信号通常在终端关闭或进程被挂断时发送给进程,设置为忽略后,进程不会因终端关闭而退出。
      其次,signal(SIGPIPE, SIG_IGN) 表示忽略 SIGPIPE 信号。SIGPIPE 信号在进程向一个已关闭的套接字或无效的地址发送数据时产生,设置为忽略后,进程不会因写入无效套接字而退出。
      综上所述,这段代码的作用是使进程在终端关闭或写入无效套接字时不会退出。
      signal(SIGHUP, SIG_IGN);
      signal(SIGPIPE, SIG_IGN);
      ...
      // 接下来还是对server进行了一些初始化赋值操作
      ...
      // 获取当前进程的pid
      server.pid = getpid();
      ...

    // 设置进程的名称
    redisSetProcTitle(argv[0]);
    Redis server 在完成运行参数设置和初始化后,就可以开始处理客户端请求了。为了能持续地处理并发的客户端请求,server 在 main 函数的最后,会进入事件驱动循环机制。

  • 执行事件驱动框架

Architecture
0 声望0 粉丝

知其然, 更要知其所以然~