
Redis: under the hood---转载
http://pauladamsmith.com/articles/redis-under-the-hood.html#redis-under-the-hoodHow does the Redis server work?I was curious to learn more about Redis’s internals, so I’ve been familiarizing myself with the source, largely by reading and jumping around in Emacs. After I had peeled back enough of the onion’s layers, I realized I was trying to keep track of too many details in my head, and it wasn’t clear how it all hung together. I decided to write out in narrative form how an instance of the Redis server starts up and initializes itself, and how it handles the request/response cycle with a client, as a way of explaining it to myself, hopefully in a clear fashion. Luckily, Redis has a nice, clean code base that is easy to read and follow along. Armed with a TAGS file, my $EDITOR, and GDB, I set out to see how it all works under the hood. (Incidentally, I was working with the Redis code base as of commit b4f2e41. Of course, internals such as I outline below are subject to change. However, the broad architecture of the server is unlikely to change very much, and I tried to keep that in mind as I went along.)This article examines server startup and takes a high-level view of the request/response processing cycle. In a subsequent article, I’ll dive in to greater detail and trace a simple SET/GET command pair as they make their way through Redis.Redis: under the hoodStartupBeginning global server state initializationSetting up command tableLoading config fileinitServer()Shared objectsShared integersEvent loopDatabasesTCP socketServer cronRegistering connection handler with event loopOpening the AOFBack up to main()Restoring dataEvent loop setupEntering the event loopProcessing a request & returning a responseHandling a new connectionReading a command from a clientExecuting the command and respondingSummaryNext time — tracing a SET and GETStartupLet’s begin with the main() function in redis.c.Beginning global server state initializationFirst, initServerConfig() is called. This partially initializes a variable server, which has the type struct redisServer, that serves as the global server state.// redis.h:338struct redisServer {pthread_t mainthread;int port;int fd;redisDb *db;// ...};// redis.c:69struct redisServer server; /* server global state */There are a huge number of members in this struct, but they generally fall into the following categories:general server statestatisticsconfiguration from config filereplicationsort parametersvirtual memory config, state, I/O threads, & statszip structureevent loop helperspub/subFor example, this struct includes members that map to options in the configuration file (usually named redis.conf) such as the port the server listens on and how verbose logging should be, pointers to linked lists of connected clients and slave Redis servers as well as the Redis database(s) itself, and counters for statistics like the number of commands processed since startup.initServerConfig() provides default values for the members that can be configured by the user via the redis.conf config file.Setting up command tableThe next thing main() does is sort the table of Redis commands. These are defined in a global variable readonlyCommandTable which is an array of struct redisCommands.// redis.c:70struct redisCommand *commandTable;struct redisCommand readonlyCommandTable[] = {{"get",getCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},{"set",setCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},{"setnx",setnxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},{"setex",setexCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},{"append",appendCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},// ...};// redis.h:458typedef void redisCommandProc(redisClient *c);// ...struct redisCommand {char *name;redisCommandProc *proc;int arity;int flags;// ...};A redisCommand struct keeps track of its name — the mnemonic, i.e., “get” — a pointer to the actual C function that performs the command, the command’s arity, command flags such as whether it returns a bulk response, and a number of VM-specific members.)The read-only table is ordered in source code so that commands are grouped by category, such as string commands, list commands, set commands, etc., to make it easier for a programmer to scan the table for similar commands. The sorted table of commands is pointed to by the global variable commandTable, and is used to lookup Redis commands with a standard binary search (lookupCommand(), which returns a pointer to a redisCommand).Loading config filemain() moves on to processing command-line options given by the user starting up the redis-server executable. Currently, there is only a single argument that Redis takes — aside from the usual version -vand help -h flags — which is a path to a config file. If the path is given, Redis loads the config file and overrides any defaults already set by initServerConfig() by calling loadServerConfig(). This function is fairly straightforward, looping over each line in the config file and converting values that match directive names to appropr