[Redis Foundation] Startup Process

Keywords: Redis Database socket Unix

1. Initialize server state structure

The initial server variable is completed by the redis.c/initServerConfig function with the following source code

void initServerConfig() {
    int j;

    // Server status

    // Setting the running ID of the server
    getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
    // Setting default profile path
    server.configfile = NULL;
    // Set default server frequency
    server.hz = REDIS_DEFAULT_HZ;
    // Add a trailing character to the running ID
    server.runid[REDIS_RUN_ID_SIZE] = '\0';
    // Setting up the Server's Running Architecture
    server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
    // Setting default server port number
    server.port = REDIS_SERVERPORT;
    server.tcp_backlog = REDIS_TCP_BACKLOG;
    server.bindaddr_count = 0;
    //Many lines are omitted from the middle
    server.loading_process_events_interval_bytes = (1024*1024*2);

    // Initialization LRU time
    server.lruclock = getLRUClock();

    // Initialize and set save conditions
    resetServerSaveParams();

    appendServerSaveParams(60*60,1);  /* save after 1 hour and 1 change */
    appendServerSaveParams(300,100);  /* save after 5 minutes and 100 changes */
    appendServerSaveParams(60,10000); /* save after 1 minute and 10000 changes */

    /* Replication related */
    // Initialization and replication-related States
    server.masterauth = NULL;
    server.masterhost = NULL;
    server.masterport = 6379;
    server.master = NULL;
    server.cached_master = NULL;
    server.repl_master_initial_offset = -1;
    server.repl_state = REDIS_REPL_NONE;
    server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
    server.repl_serve_stale_data = REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA;
    server.repl_slave_ro = REDIS_DEFAULT_SLAVE_READ_ONLY;
    server.repl_down_since = 0; /* Never connected, repl is down since EVER. */
    server.repl_disable_tcp_nodelay = REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY;
    server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;
    server.master_repl_offset = 0;

    /* Replication partial resync backlog */
    // backlog used to initialize the PSYNC command
    server.repl_backlog = NULL;
    server.repl_backlog_size = REDIS_DEFAULT_REPL_BACKLOG_SIZE;
    server.repl_backlog_histlen = 0;
    server.repl_backlog_idx = 0;
    server.repl_backlog_off = 0;
    server.repl_backlog_time_limit = REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT;
    server.repl_no_slaves_since = time(NULL);

    /* Client output buffer limits */
    // Setting Output Buffer Limits for Clients
    for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++)
        server.client_obuf_limits[j] = clientBufferLimitsDefaults[j];

    /* Double constants initialization */
    // Initialization of floating-point constants
    R_Zero = 0.0;
    R_PosInf = 1.0/R_Zero;
    R_NegInf = -1.0/R_Zero;
    R_Nan = R_Zero/R_Zero;

    /* Command table -- we initiialize it here as it is part of the
     * initial configuration, since command names may be changed via
     * redis.conf using the rename-command directive. */
    // Initialization Command Table
    // Initialization here is because these commands may be used when reading the.conf file next.
    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");

    /* Slow log */
    // Initialize slow query log
    server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;
    server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN;

    /* Debugging */
    // Initialize debugging items
    server.assert_failed = "<no assertion failed>";
    server.assert_file = "<no file>";
    server.assert_line = 0;
    server.bug_report_start = 0;
    server.watchdog_period = 0;
}

Analysis: From the source code of the function, we can see that its main work is as follows:

  • Setting the running ID of the server
  • Set the default running frequency of the server
  • Set the default file configuration path for the server
  • Setting up the Server's Running Architecture
  • Set the default port number of the server
  • Set the default RDB and AOF persistence conditions for the server
  • Initialize the server's LRU clock
  • Create a command table

2. Load configuration options

  • Specify configuration parameters such as redis-server-port 10086

  • Specify configuration files: such as redis-server redis.conf

3. Initialization of Server Data Structure

(1) In the first step, the initServerConfig function only creates the command table, while the server state also contains other data structures, such as server.clients linked list, server.db array, server.pubsub_channels dictionary, server.pubsub_patterns linked list, server.lua environment, server.log slow query log.

(2) In the third step, the server calls the initServerConfig function to allocate memory for the above data structures.

(3) After that, the initServer function is responsible for initializing the data structure, and there are several important operations.

  • Setting Process Signal Processor for Server
  • Create shared objects, such as ok, err, strings of integers 1-10000, etc.
  • Open the listening port of the server
  • Create time events for serverCron functions
  • If an AOF file exists, open the AOF file, and if not, create a new AOF file.
  • Background I/O module (bio) to initialize the server

The source code of initServer function is as follows:

void initServer() {
    int j;

    // Setting Signal Processing Function
    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    setupSignalHandlers();

    // Setting syslog
    if (server.syslog_enabled) {
        openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT,
            server.syslog_facility);
    }

    // Initialize and create data structures
    server.current_client = NULL;
    server.clients = listCreate();
    server.clients_to_close = listCreate();
    server.slaves = listCreate();
    server.monitors = 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;

    // Create shared objects
    createSharedObjects();
    adjustOpenFilesLimit();
    server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR);
    server.db = zmalloc(sizeof(redisDb)*server.dbnum);

    /* Open the TCP listening socket for the user commands. */
    // Open the TCP listening port to wait for the client's command request
    if (server.port != 0 &&
        listenToPort(server.port,server.ipfd,&server.ipfd_count) == REDIS_ERR)
        exit(1);

    /* Open the listening Unix domain socket. */
    // Open UNIX Local Port
    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) {
            redisLog(REDIS_WARNING, "Opening 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) {
        redisLog(REDIS_WARNING, "Configured to not listen anywhere, exiting.");
        exit(1);
    }

    /* Create the Redis databases, and initialize other internal state. */
    // Create and initialize database structure
    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(&setDictType,NULL);
        server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
        server.db[j].eviction_pool = evictionPoolAlloc();
        server.db[j].id = j;
        server.db[j].avg_ttl = 0;
    }

    // Creating PUBSUB Related Structures
    server.pubsub_channels = dictCreate(&keylistDictType,NULL);
    server.pubsub_patterns = listCreate();
    listSetFreeMethod(server.pubsub_patterns,freePubsubPattern);
    listSetMatchMethod(server.pubsub_patterns,listMatchPubsubPattern);

    server.cronloops = 0;
    server.rdb_child_pid = -1;
    server.aof_child_pid = -1;
    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.resident_set_size = 0;
    server.lastbgsave_status = REDIS_OK;
    server.aof_last_write_status = REDIS_OK;
    server.aof_last_write_errno = 0;
    server.repl_good_slaves_count = 0;
    updateCachedTime();

    /* Create the serverCron() time event, that's our main way to process
     * background operations. */
    // Create time events for serverCron()
    if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
        redisPanic("Can't create the serverCron time event.");
        exit(1);
    }

    /* Create an event handler for accepting new connections in TCP and Unix
     * domain sockets. */
    // Connection Acceptance Processor for TCP Connection
    // connect() calls to accept and respond to clients
    for (j = 0; j < server.ipfd_count; j++) {
        if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
            acceptTcpHandler,NULL) == AE_ERR)
            {
                redisPanic(
                    "Unrecoverable error creating server.ipfd file event.");
            }
    }

    // Associate the Response Processor for Local Sockets
    if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
        acceptUnixHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.sofd file event.");

    /* Open the AOF file if needed. */
    // If AOF persistence is turned on, open or create an AOF file
    if (server.aof_state == REDIS_AOF_ON) {
        server.aof_fd = open(server.aof_filename,
                               O_WRONLY|O_APPEND|O_CREAT,0644);
        if (server.aof_fd == -1) {
            redisLog(REDIS_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. */
    // For 32-bit instances, by default the maximum available memory is limited to 3 GB
    if (server.arch_bits == 32 && server.maxmemory == 0) {
        redisLog(REDIS_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 = REDIS_MAXMEMORY_NO_EVICTION;
    }

    // If the server opens in cluster mode, initialize the cluster
    if (server.cluster_enabled) clusterInit();

    // Initialize script caching related to replication functionality
    replicationScriptCacheInit();

    // Initialization script system
    scriptingInit();

    // Initialize slow query function
    slowlogInit();

    // Initialization of BIO System
    bioInit();
}
  • When the initServer function is executed, the server status is as follows:

4. Restoring database state

  • If the server has enabled AOF persistence function, then restore the database state with AOF file, otherwise restore the database state with RDB file.

  • The following figure shows how long it takes the server to print out the loaded file in the log and restore the database state.

5. Executing Event Loop

  • In the last step of initialization, the server prints the following log and executes the server's event loop



I am a skilled student, if there are any mistakes, please point out, thank you!
If you have better suggestions, you can leave a message for us to discuss and make progress together.
Thank you for your patience in reading this blog!

Reference Books: Redis Design and Implementation (Second Edition) - Huang Jianhong

Posted by Ang3l0fDeath on Mon, 10 Jun 2019 11:47:33 -0700