• 二、Redis服务启动以及请求流程


    上一次我们大概熟悉了一下redis的目录结构,里面提到了服务启动相关的源码主要包含如下两个文件:

     找到server.c文件中的main函数开始了解服务的启动过程:

    源码解析:

    1、主流程:

    /* Global vars */ // 全局变量
    struct redisServer server; /* Server global state */
    volatile unsigned long lru_clock; /* Server global current LRU time. */
    struct redisCommand redisCommandTable[] ={...} /* Our command table.*/
    int main(int argc, char **argv) { // 主入口
        struct timeval tv;
        int j;
    
    #ifdef REDIS_TEST // redis测试环境设置
        if (argc == 3 && !strcasecmp(argv[1], "test")) {
            ...return -1; /* test not found */
        }
    #endif
    
        /* We need to initialize our libraries, and the server configuration. */ // 初始化库和服务器配置。  
    #ifdef INIT_SETPROCTITLE_REPLACEMENT
        spt_init(argc, argv);
    #endif
        // 设置相关默认值
       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); // 哨兵模式
    server.sentinel_mode
    = checkForSentinelMode(argc,argv); // 默认配置加载
    initServerConfig(); moduleInitModulesSystem();
    /* Store the executable path and arguments in a safe place in order * to be able to restart the server later. */ 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]); /* We need to init sentinel right now as parsing the configuration file * in sentinel mode will have the effect of populating the sentinel * data structures with master nodes to monitor. */ if (server.sentinel_mode) { initSentinelConfig(); initSentinel(); }
       // 加载配置文件
    /* 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); if (argc >= 2) { j = 1; /* First option to parse in argv[] */ sds options = sdsempty(); char *configfile = NULL; /* 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? */ 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++; } /* All the other options are parsed and conceptually appended to the * configuration file. For instance --port 6380 will generate the * string "port 6380\n" to be parsed after the actual file name * 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(); #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__ */ // 数据恢复
         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); // src/ae.c 主循环服务
        // 关闭服务
       aeDeleteEventLoop(server.el);
    return 0; }

    2、配置参数

      // 默认配置加载
    initServerConfig(); 核心代码对象赋值(
    struct redisServer server;)
      // 加载配置文件
      loadServerConfig(configfile,options) 》》》
    void loadServerConfigFromString(char *config){ ... strcasecmp(argv[0],"port") ... } 配置文件加载数据

     3、初始化服务流程

     

    void initServer(void) {
        int j;
    
        // 信号函数
    signal(SIGHUP, SIG_IGN); // SIG_IGN表示忽略SIGHUP那个注册的信号 SIGHUP和控制台操作有关 signal(SIGPIPE, SIG_IGN); // SIGPIPE 信号时就不会中止程序 setupSignalHandlers();
    if (server.syslog_enabled) { openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT, server.syslog_facility); } // 客户端配置参数
       server.hz
    = server.config_hz; server.pid = getpid(); server.current_client = NULL; server.fixed_time_expire = 0; server.clients = listCreate(); server.clients_index = raxNew(); 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(); adjustOpenFilesLimit(); // 创建事件对象
       server.el
    = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR); // aeEventLoop 后续进行任务处理的重要数据结构


     if (server.el == NULL) {

            serverLog(LL_WARNING,
                "Failed creating the event loop. Error message: '%s'",
                strerror(errno));
            exit(1);
        }
        // 创建DB对象 默认值dbnum:16
       server.db
    = zmalloc(sizeof(redisDb)*server.dbnum); /* Open the TCP listening socket for the user commands. */
    // 监听客户端输入的请求指令
       if (server.port != 0 && listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR) exit(1); /* Open the listening Unix domain socket. */ if (server.unixsocket != NULL) { unlink(server.unixsocket); /* don't care if this fails */ server.sofd = anetUnixServer(server.neterr,server.unixsocket, server.unixsocketperm, server.tcp_backlog); if (server.sofd == ANET_ERR) { serverLog(LL_WARNING, "Opening Unix socket: %s", server.neterr); exit(1); } anetNonBlock(NULL,server.sofd); } /* Abort if there are no listening sockets at all. */ if (server.ipfd_count == 0 && server.sofd < 0) { serverLog(LL_WARNING, "Configured to not listen anywhere, exiting."); exit(1); } /* Create the Redis databases, and initialize other internal state. */
      // 创建数据库,初始化参数
      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.db[j].defrag_later = listCreate(); } evictionPoolAlloc(); /* Initialize the LRU keys pool. */ // 发布/订阅配置 publish/subscribe 初始化参数
       server.pubsub_channels
    = dictCreate(&keylistDictType,NULL); server.pubsub_patterns = listCreate(); listSetFreeMethod(server.pubsub_patterns,freePubsubPattern); listSetMatchMethod(server.pubsub_patterns,listMatchPubsubPattern); server.cronloops = 0; // rdb aof 初始化参数
       server.rdb_child_pid
    = -1; server.aof_child_pid = -1; server.rdb_child_type = RDB_CHILD_TYPE_NONE; server.rdb_bgsave_scheduled = 0; server.child_info_pipe[0] = -1; server.child_info_pipe[1] = -1; server.child_info_data.magic = 0; aofRewriteBufferReset(); server.aof_buf = sdsempty(); server.lastsave = time(NULL); /* At startup we consider the DB saved. */ server.lastbgsave_try = 0; /* At startup we never tried to BGSAVE. */ server.rdb_save_time_last = -1; server.rdb_save_time_start = -1; server.dirty = 0; resetServerStats(); /* A few stats we don't want to reset: server startup time, and peak mem. */ server.stat_starttime = time(NULL); server.stat_peak_memory = 0; server.stat_rdb_cow_bytes = 0; server.stat_aof_cow_bytes = 0; server.cron_malloc_stats.zmalloc_used = 0; server.cron_malloc_stats.process_rss = 0; server.cron_malloc_stats.allocator_allocated = 0; server.cron_malloc_stats.allocator_active = 0; server.cron_malloc_stats.allocator_resident = 0; server.lastbgsave_status = C_OK; server.aof_last_write_status = C_OK; server.aof_last_write_errno = 0; server.repl_good_slaves_count = 0; /* Create the timer callback, this is our way to process many background * operations incrementally, like clients timeout, eviction of unaccessed * expired keys and so forth. */
      // 创建定时器 操作后台事务 key过期、客户端超时等问题
      
       // 定时方法 long long aeCreateTimeEvent(aeEventLoop *eventLoop-循环事件, long long milliseconds-毫秒,
            aeTimeProc *proc-待处理的任务流程, void *clientData-客户端数据,
            aeEventFinalizerProc *finalizerProc-终止流程)

       if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) { serverPanic("Can't create event loop timers."); exit(1); } /* Create an event handler for accepting new connections in TCP and Unix * domain sockets. */ for (j = 0; j < server.ipfd_count; j++) {
          // 创建文件事件监控
          if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE, acceptTcpHandler,NULL) == AE_ERR)
                {
                    serverPanic(
                        "Unrecoverable error creating server.ipfd file event.");
                }
        }
        if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
            acceptUnixHandler,NULL) == AE_ERR) serverPanic("Unrecoverable error creating server.sofd file event.");
    
    
        /* Register a readable event for the pipe used to awake the event loop
         * when a blocked client in a module needs attention. */
        if (aeCreateFileEvent(server.el, server.module_blocked_pipe[0], AE_READABLE,
            moduleBlockedClientPipeReadable,NULL) == AE_ERR) {
                serverPanic(
                    "Error registering the readable event for the module "
                    "blocked clients subsystem.");
        }
    
        /* Open the AOF file if needed. */
        
       // AOF功能开启执行此逻辑
       if (server.aof_state == AOF_ON) { server.aof_fd = open(server.aof_filename, O_WRONLY|O_APPEND|O_CREAT,0644); if (server.aof_fd == -1) { serverLog(LL_WARNING, "Can't open the append-only file: %s", strerror(errno)); exit(1); } } /* 32 bit instances are limited to 4GB of address space, so if there is * no explicit limit in the user provided configuration we set a limit * at 3 GB using maxmemory with 'noeviction' policy'. This avoids * useless crashes of the Redis instance for out of memory. */ if (server.arch_bits == 32 && server.maxmemory == 0) { serverLog(LL_WARNING,"Warning: 32 bit instance detected but no memory limit set. Setting 3 GB maxmemory limit with 'noeviction' policy now."); server.maxmemory = 3072LL*(1024*1024); /* 3 GB */ server.maxmemory_policy = MAXMEMORY_NO_EVICTION; }
       // 集群初始化
       if (server.cluster_enabled) clusterInit(); replicationScriptCacheInit(); // lua脚本执行
       scriptingInit(
    1); // 开启运维功能
    slowlogInit(); latencyMonitorInit(); }
    void InitServerLast() {
        bioInit(); // 创建线程任务 Redis6.0版本此步骤后会多一步多IO处理函数initThreadedIO()
        server.initial_memory_usage = zmalloc_used_memory();
    }
    /* Initialize the background system, spawning the thread. */
    void bioInit(void) {
        pthread_attr_t attr;
        pthread_t thread;
        size_t stacksize;
        int j;
    
        /* Initialization of state vars and objects */
        for (j = 0; j < BIO_NUM_OPS; j++) {
            pthread_mutex_init(&bio_mutex[j],NULL);
            pthread_cond_init(&bio_newjob_cond[j],NULL);
            pthread_cond_init(&bio_step_cond[j],NULL);
            bio_jobs[j] = listCreate();
            bio_pending[j] = 0;
        }
    
        /* Set the stack size as by default it may be small in some system */
        pthread_attr_init(&attr);
        pthread_attr_getstacksize(&attr,&stacksize);
        if (!stacksize) stacksize = 1; /* The world is full of Solaris Fixes */
        while (stacksize < REDIS_THREAD_STACK_SIZE) stacksize *= 2;
        pthread_attr_setstacksize(&attr, stacksize);
    
        /* Ready to spawn our threads. We use the single argument the thread
         * function accepts in order to pass the job ID the thread is
         * responsible of. */
        for (j = 0; j < BIO_NUM_OPS; j++) {
            void *arg = (void*)(unsigned long) j;
            if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg) != 0) {
                serverLog(LL_WARNING,"Fatal: Can't initialize Background Jobs.");
                exit(1);
            }
            bio_threads[j] = thread;
        }
    }
  • 相关阅读:
    使用JdbcTemplate访问数据库
    解决为什么每次打开Eclipse新的workspace需要更新nexus-maven-repository-index问题
    java内存设置
    Eclipse如何解决启动慢
    eclipse的包的加减号展开方式
    maven总结5
    maven总结4
    maven总结3
    maven总结2
    maven总结1
  • 原文地址:https://www.cnblogs.com/chch213/p/16251375.html
Copyright © 2020-2023  润新知