Inside Redis: How Initialization and Event Loop Power the Server
This article explains Redis's startup sequence, detailing server initialization, configuration loading, event‑loop creation, supported I/O multiplexing mechanisms, timer and I/O callbacks, and the complete request‑response flow from client command to server reply.
Initialization Process and Event Loop Overview
Redis's main function resides in src/server.c and after start-up follows two phases: server initialization (including event‑loop setup) and event‑loop execution. The flow is illustrated by the diagram below.
1. Initialization Stage Details
Configuration Loading and Server Structure Redis uses a struct redisServer to hold all runtime parameters such as listening port, client list, command table, persistence settings, and the event loop pointer. Key fields include configfile , commands , el , port , and AOF related members.
The command table is populated by populateCommandTable() , enabling lookup of command metadata (argument count, handler function, etc.).
Configuration from redis.conf overrides the defaults in redisServer .
2. Event Loop Creation
Redis implements its own event‑driven library in ae.c, abstracting OS‑specific I/O multiplexing mechanisms. Supported mechanisms are:
select
epoll (Linux‑specific, replaces select/poll)
kqueue (FreeBSD/macOS)
event ports
On macOS the library selects kqueue; on Linux it selects epoll. The epoll API is described below:
int epoll_create(int size); // size ignored since kernel 2.6.8</code><code>int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); // op: EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL</code><code>int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); // timeout -1 = block forever, 0 = return immediatelyThe event loop is represented by the aeEventLoop struct:
typedef struct aeEventLoop {
int maxfd; /* highest fd currently registered */
int setsize; /* max number of fds tracked */
long long timeEventNextId;
time_t lastTime; /* used to detect clock skew */
aeFileEvent *events; /* registered events */
aeFiredEvent *fired; /* fired events */
aeTimeEvent *timeEventHead;
int stop;
void *apidata; /* polling‑API‑specific data */
aeBeforeSleepProc *beforesleep;
aeBeforeSleepProc *aftersleep;
} aeEventLoop;Call chain on Linux:
main() → initServer() → aeCreateEventLoop() → aeApiCreate(eventLoop) → epoll_create()The resulting aeApiState struct (holding epfd and event array) is stored in aeEventLoop->apidata, and the loop itself is saved in redisServer->el. The default maximum number of file descriptors handled is 10,000 + RESERVED_FDS (32) + 96 safety margin.
3. Socket Listening
TCP Listening listenToPort() reads redis.conf and creates a TCP socket, storing the descriptor in server.ipfd .
Unix‑Domain Listening anetUnixServer() creates a Unix‑domain socket for efficient inter‑process communication, stored in server.sofd .
4. Timer Event Registration
Redis registers a timer event that periodically invokes serverCron(). Tasks performed each cycle include key expiration, statistics update, master‑slave reconnection, cluster node reconnection, and triggering BGSAVE/AOF rewrites.
Timer handling distinguishes three cases:
Next timer is in the future – convert its timestamp to a blocking timeout.
Next timer is overdue – set timeout to zero to fire immediately.
No timers – set timeout to infinite, waking only on I/O events.
5. I/O Event Registration
TCP accept handler: acceptTcpHandler() (defined in networking.c).
Unix‑domain accept handler: acceptUnixHandler().
6. Redis Command Processing Flow
The client‑server interaction consists of two stages:
Connection establishment (TCP or Unix socket).
Command send, execution, and response.
When data arrives, the event loop triggers the readQueryFromClient callback. The server reads a byte stream, parses complete commands, handles possible “sticky packet” (粘包) situations, looks up the command in the pre‑populated command table, executes it, and writes the result into an output buffer. A separate I/O event later flushes this buffer to the client.
Key points:
Data is read as a stream; partial commands may arrive.
If a full command cannot be parsed, processing pauses until more data arrives.
When multiple commands are present, the server parses all complete commands, leaving any trailing partial data for the next read.
Command lookup uses the global redisCommandTable populated earlier.
Sending the response is driven by another event‑loop iteration.
Flowchart images illustrate the connection registration, command execution, and response stages:
Summary
Server initialization steps after main() starts.
Event‑loop creation, supported I/O multiplexing, and timer/I/O callbacks.
Full lifecycle of a Redis command from client receipt, parsing, execution, to response delivery.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
