Redis tutorial command execution process details
- 2020-06-07 05:30:48
- OfStack
preface
I've written a series of articles that have explored Redis's data structure, database implementation, key's expiration policy, and how Redis handles events. So we are only one step away from a stand-alone implementation of Redis, which is how Redis handles the commands sent by client and returns the results, so let's take a closer look at how Redis executes the commands under 1.
Read this article and you will learn:
How does Redis execute commands sent from remote clientsRedis client (Client)
Redis is a single-threaded application. How does it link to multiple client resume networks and process commands?
Because Redis is based on I/O multiplexing, in order to be able to handle multiple client requests, Redis locally creates an redisClient data structure for every client linked to the Redis server, which contains each client's individual state and commands executed. The Redis server USES one linked list to maintain multiple redisClient data structures.
On the server side, a linked list is used to manage all redisClient.
struct redisServer {
//...
list *clients; /* List of active clients */
//...
}
So I looked at the data structures and important parameters that redisClient contains:
typedef struct redisClient {
// Client status flag
int flags; /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... */
// Socket descriptor
int fd;
// The database currently in use
redisDb *db;
// Of the database currently in use id (number)
int dictid;
// Client name
robj *name; /* As set by CLIENT SETNAME */
// Query buffer
sds querybuf;
// Query buffer length peak
size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size */
// The number of arguments
int argc;
// Parameter object array
robj **argv;
// Records the commands executed by the client
struct redisCommand *cmd, *lastcmd;
// Type of request: Inline or multiple commands
int reqtype;
// The number of remaining unread command contents
int multibulklen; /* number of multi bulk arguments left to read */
// The length of the command content
long bulklen; /* length of bulk argument in multi bulk request */
// Reply to the list
list *reply;
// Returns the total size of the objects in the list
unsigned long reply_bytes; /* Tot bytes of objects in reply list */
// Bytes sent, processed short write with
int sentlen; /* Amount of bytes already sent in the current
buffer or object being sent. */
// Return offset
int bufpos;
// Recovery buffer
char buf[REDIS_REPLY_CHUNK_BYTES];
// ...
}
It is important to note here that redisClient does not refer to the remote client, but to the local data structure of the 1 Redis service, which we understand to be a mapping or proxy for the remote client.
flags
flags represents the role of the current client and its current state. It's special that it can represent one state alone or more states.
querybuf
querybuf is an sds dynamic string type. The buf specification is that it is just a buffer for storing commands that are not parsed.
argc & argv
The above querybuf is an unprocessed command. When Redis parses the querybuf command, the number and parameters obtained will be saved in argc and argv respectively. argv is an array of 1 redisObject.
cmd
Redis saved all redisCommand using one dictionary. key is the name of redisCommand, and the value is 1 redisCommand structure. This structure holds the implementation function of the command, the flag of the command, the number of arguments that the command should be given, the execution times of the command, the total elapsed time and other statistical information. cmd is 1 redisCommand.
When Redis resolves argv and argc, it will query the corresponding redisCommand in the dictionary according to the array argv[0]. In the example above, Redis will look up redisCommand corresponding to the command SET in the dictionary. redis executes the implementation functions of the commands in redisCommand.
buf & bufpos & reply
buf is an array of length REDIS_REPLY_CHUNK_BYTES. After Redis performs the corresponding operation, the returned data will be stored in buf. bufpos is used to record the number of bytes used in buf. When the data to be recovered is greater than REDIS_REPLY_CHUNK_BYTES, redis will use the linked list of reply to store the data.
The other parameters
The other parameters you can see by looking at the comments, literally. The omitted parameters basically relate to the Redis cluster management parameters, which will be covered in future articles.
Client connection and disconnection
As mentioned above, redisServer USES one linked list to maintain all redisClient states. Every time a client initiates a link, one corresponding redisClient data structure will be generated in Redis and added to the linked list of clients.
A client is likely to be disconnected for a variety of reasons.
There are several types:
Client actively exits or is kill. timeout timeout. Redis to protect itself, will break the development of data over the limit size of the client. To protect itself, Redis interrupts clients that need to return more data than the limit size.Call summary
When the nested words on the client and server sides become readable, the server invokes the command request handler to do the following:
Reads the data in the nested words and writes to querybuf. Parse the commands in querybuf and record them in argc and argv. Find the corresponding recommand according to argv[0]. Execute the implementation functions corresponding to recommand. After execution, the results are stored in buf & bufpos & In reply, back to the caller.Redis Server (server)
The above article observed the execution of commands from the perspective of redisClient. The following part of this article will observe how Redis implements the execution of commands from the perspective of Redis code.
redisServer startup
Before you understand how redisServer works, you need to understand what the startup of redisServer does:
You can continue to observe the main() function of Redis.
int main(int argc, char **argv) {
//...
// Creates and initializes the server data structure
initServer();
//...
}
We'll focus on the function initServer(), which initializes the server's data structure. Continue tracking code:
void initServer() {
//...
// create eventLoop
server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR);
/* Create an event handler for accepting new connections in TCP and Unix
* domain sockets. */
// for TCP Connection associated connection response ( accept ) processor
// Used to receive and reply to clients connect() call
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 reply handler for the local socket
if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
acceptUnixHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.sofd file event.");
//...
}
Due to space limitations, we have omitted a lot of code irrelevant to this article and kept the core logic code.
In the previous article, The Event-driven Model in Redis, we explained that redis USES different event handlers to handle different events.
In this code:
eventLoop for the event handler was initialized Two event handlers, acceptTcpHandler and acceptUnixHandler, are registered with eventLoop to handle remote and local links, respectively.The creation of redisClient
The acceptTcpHandler event handler is triggered when a remote client connects to the Redis server.
The acceptTcpHandler event handler creates a link. Then proceed to call acceptCommonHandler.
The acceptCommonHandler event handler is used to:
Call the createClient() method to create redisClient Check that the amount of redisClient that has been created exceeds the maximum number of server allowed If the limit is exceeded, the remote connection is denied Otherwise create redisClient created successfully And updates the count of connections, updating the flags field of redisClinetAt this time, Redis creates the redisClient data structure on the server side, and at this time, the remote client creates a proxy in redisServer. The remote client then establishes contact with the Redis server and sends a command to the server.
Processing command
In createClient() row count:
// Bind reads events to events loop (Start receiving command requests)
if (aeCreateFileEvent(server.el,fd,AE_READABLE,readQueryFromClient, c) == AE_ERR)
readQueryFromClient is registered with eventLoop. The purpose of readQueryFromClient is to read the client's query buffer contents from client.
The function processInputBuffer is then called to handle the client request. There are several core functions in processInputBuffer:
processInlineBuffer and processMultibulkBuffer parse the commands in querybuf and record them in argc and argv. processCommand finds the corresponding recommen according to argv[0], and executes the corresponding execution function of recommend. The correctness of the command is also verified before execution. Save the results to buf & bufpos & In the replyReturn the data
When you're done, you need to return the data to the remote caller. The call chain is as follows
processCommand - > addReply - > prepareClientToWrite
In prepareClientToWrite we have the familiar code:
aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,sendReplyToClient, c) == AE_ERR) return REDIS_ERR;
The sendReplyToClient event handler is bound to eventloop.
By observing the code in sendReplyToClient, it is found that if bufpos is greater than 0, buf will be sent to the remote client; if the length of reply is greater than 0, reply will be traversed and sent to the remote client. It should be noted here that in order to avoid the excessive amount of DATA in reply, Redis will be excessively occupied and slow accordingly. To solve this problem, when the total number of writes is greater than REDIS_MAX_WRITE_PER_EVENT, Redis temporarily interrupts the write, records the progress of the operation, gives the processing time to other operations, and the rest is left to continue next time. This routine we have seen too many road.
conclusion
When the remote client connects to redis, the redis server creates an redisClient proxy for the remote client. redis reads the data in the nested words and writes to querybuf. Parse the commands in querybuf and record them in argc and argv. Find the corresponding recommand according to argv[0]. Execute the corresponding execution function of recommend. After execution, save the results to buf & bufpos & reply. Returns to the caller. When the data is returned, the size of the written data will be controlled. Guarantee the corresponding time of redis.As a single-threaded application, Redis follows the idea that each step has an upper limit (including the upper limit of execution time or the upper limit of file size). Once the upper limit is reached, the current progress will be recorded and executed next time. This ensures that Redis can respond in a timely manner without blocking.