Redis Learning Note (a)---server basic process

Source: Internet
Author: User
Tags epoll set time
server workflow
After executing ./redis-server, the server side of the redis database will start.
Then the main () function in redis.c will be executed
The work in the main () function can be divided into the following parts:


1. Initialize the configuration information on the server side---initServerConfig ()
2. Analyze the command parameters at run time and process according to the parameters, eg: ./redis-server--help
3. If the daemonize parameter is set, set the server to the deamon process-daemonize ()
4. Start server---initServer ()
5. Set the periodic processing function beforeSleep ()
6. Start working---aeMain ()
1. initServerConfig ()
Initialize the configuration information on the server side, and save it in the server instance server, including the listening port, DB number, command table and other information.


2. daemonize ()
Set the process as a daemon.
Related knowledge of the daemon process has been briefly introduced in the Linux process foundation.

void daemonize (void) {
    int fd;
    if (fork ()! = 0) exit (0); / * parent exits * /
    setsid (); / * create a new session * /
    if ((fd = open ("/ dev / null", O_RDWR, 0))! = -1) {
        dup2 (fd, STDIN_FILENO);
        dup2 (fd, STDOUT_FILENO);
        dup2 (fd, STDERR_FILENO);
        if (fd> STDERR_FILENO) close (fd);
    }
}
3. initServer ()
This function has completed a lot of tasks, including setting up signal processing functions, creating clients queue, slaves queue, creating database, creating shared objects, etc.
In addition, the two most important tasks are to create listening sockets and listening to clients, and to create periodic processing events.
We know that the events of any server can be divided into IO read and write events and time processing events, as does redis.
1) IO read and write events, including monitoring client connections and data interaction with clients, etc.
2) Time processing events, processing related events at a set time, including periodically refreshing the database, etc.
Both types of events are saved through the variable server.el.

3.1, IO read and write events
First of all, redis will create a monitoring fd for the server to monitor the connection from the client, and it will mainly call the following two functions

listenToPort (server.port, server.ipfd, & server.ipfd_count);
aeCreateFileEvent (server.el, server.ipfd [j], AE_READABLE, acceptTcpHandler, NULL);
listenToPort: Create a listener descriptor based on the incoming port, call fcntl () to set fd as non-blocking, and then call the bind () and listen () functions. Note that redis will create a socket respectively based on IPv4 and IPv6 addresses
aeCreateFileEvent: Create read and write events. For the listener descriptor, you only need to create a read event to listen for the connection from the client. Note that the callback function of the listening descriptor is acceptTcpHandler
Finally add this event to the server.el structure
3.2 Time processing events
For the server side, redis will periodically execute serverCron () to complete some processing, so this event is also added to the server.el structure

aeCreateTimeEvent (server.el, 1, serverCron, NULL, NULL);
// Execute serverCron () every 1ms
4. beforeSleep ()
In the redis event main loop, each loop will be executed once, including sending ACK to all slaves, writing AOF files, etc.

5. aeMain ()
The most important function of the redis server is the main loop of redis events. If redis does not receive an interrupt signal, it will continue to execute this function.

aeMain (server.el);
void aeMain (aeEventLoop * eventLoop) {
    eventLoop-> stop = 0;
    while (! eventLoop-> stop) {
        if (eventLoop-> beforesleep! = NULL)
            eventLoop-> beforesleep (eventLoop);
        aeProcessEvents (eventLoop, AE_ALL_EVENTS);
    }
}
It can be found that this function is the execution of endless loop beforesleep () and aeProcessEvents ().
As the core process of the redis server, the implementation code of aeProcessEvents () is long, but there are only three actions


1. Calculate the time that calls to select, epoll and other functions can block
2. Call aeApiPoll () to wait for the IO event to occur. If an event occurs, call the corresponding callback function
3. Call processTimeEvents () to process time events

Due to the IO multiplexing mechanism such as select and epoll, when there is no event within a certain period of time, it will always block there. Therefore, in order not to affect the processing of subsequent time events, the call of the IO multiplexing mechanism must be completed before the arrival of the latest time event. Therefore, first find the latest time event and calculate the time difference from the current time as a parameter to call aeApiPoll ().

int aeProcessEvents (aeEventLoop * eventLoop, int flags)
{
    if (eventLoop-> maxfd! = -1 ||
        ((flags & AE_TIME_EVENTS) &&! (flags & AE_DONT_WAIT))) {
        // 1, when there is a time event, calculate the time difference
        if (flags & AE_TIME_EVENTS &&! (flags & AE_DONT_WAIT))
            shortest = aeSearchNearestTimer (eventLoop); // Find the most recent time event
        if (shortest) {
            aeGetTime (& now_sec, & now_ms); // Get the current time
            tvp = & tv;
            // Calculate the time difference tvp
            tvp-> tv_sec = shortest-> when_sec-now_sec;
            if (shortest-> when_ms <now_ms) {
                tvp-> tv_usec = ((shortest-> when_ms + 1000)-now_ms) * 1000;
                tvp-> tv_sec-;
            } else {
                tvp-> tv_usec = (shortest-> when_ms-now_ms) * 1000;
            }
            if (tvp-> tv_sec <0) tvp-> tv_sec = 0;
            if (tvp-> tv_usec <0) tvp-> tv_usec = 0;
        } else {
            if (flags & AE_DONT_WAIT) {// No blocking at this time, return immediately
                tv.tv_sec = tv.tv_usec = 0;
                tvp = & tv;
            } else {// otherwise you can wait forever
                tvp = NULL; / * wait forever * /
            }
        }
        // 2. Call the IO multiplexing mechanism to handle IO events
        numevents = aeApiPoll (eventLoop, tvp);
        for (j = 0; j <numevents; j ++) {
            aeFileEvent * fe = & eventLoop-> events [eventLoop-> fired [j] .fd];
            int mask = eventLoop-> fired [j] .mask;
            int fd = eventLoop-> fired [j] .fd;
            int rfired = 0;
            if (fe-> mask & mask & AE_READABLE) {// Handle read events
                rfired = 1;
                fe-> rfileProc (eventLoop, fd, fe-> clientData, mask);
            }
            if (fe-> mask & mask & AE_WRITABLE) {// Handle write events
                if (! rfired || fe-> wfileProc! = fe-> rfileProc)
                    fe-> wfileProc (eventLoop, fd, fe-> clientData, mask);
            }
            processed ++;
        }
    }
    // 3, processing time events
    if (flags & AE_TIME_EVENTS)
        processed + = processTimeEvents (eventLoop);
    return processed;
}
This is the main workflow on the redis server side.

Specific examples: 1. When a client connection arrives
At this time, there will be events in the listening descriptor of the redis server. As mentioned before, only the read event acceptTcpHandler () is registered on the fd, so execute fe-> rfileProc (eventLoop, fd, fe-> clientData, mask); Will call the acceptTcpHandler () function
1) First acceptTcpHandler () will call accept to get the connection descriptor cfd
2) Then call acceptCommonHandler () to create a client instance
3) In createClient (), the read event readQueryFromClient () is registered for each connection descriptor, and the default database of each client is set to 0
In this way, the connection between client and server is established. When a client request arrives, the readQueryFromClient () function is executed to process the request.

void acceptTcpHandler (aeEventLoop * el, int fd, void * privdata, int mask) {
    while (max--) {
        cfd = anetTcpAccept (server.neterr, fd, cip, sizeof (cip), & cport); // 1, call acc
ept get connection descriptor cfd
        if (cfd == ANET_ERR) {
            ...
        }
        acceptCommonHandler (cfd, 0);
    }
}
static void acceptCommonHandler (int fd, int flags) {
    redisClient * c;
    if ((c = createClient (fd)) == NULL) {// 2, create a client instance
        close (fd);
        return;
    }
}
redisClient * createClient (int fd) {
    redisClient * c = zmalloc (sizeof (redisClient));
    if (fd! = -1) {
        anetNonBlock (NULL, fd);
        if (aeCreateFileEvent (server.el, fd, AE_READABLE,
            readQueryFromClient, c) == AE_ERR) {// 3, register read event
            / * ...... * /
        }
    }
    selectDb (c, 0);
    / * ...... * /
    return c;
}


2. When a client request comes (eg: execute the set key value command) 2.1. ReadQueryFromClient () reads in the request and processes it
First, the read event on fd will be triggered, so the server will call readQueryFromClient () to process. The main process is:


1. Apply for memory for the receive buffer
2. Call read to read the request data from the client into c-> querybuf
3. Call processInputBuffer to process the request

void readQueryFromClient (aeEventLoop * el, int fd, void * privdata, int mask) {
    c-> querybuf = sdsMakeRoomFor (c-> querybuf, readlen); // 1, apply for space
    nread = read (fd, c-> querybuf + qblen, readlen); // 2, read request
    processInputBuffer (c); // 3, process input request
}
After executing the command set key value, print c-> querybuf to get the following result:

The format of the content is:


2.2, processInputBuffer () processing request
In processInputBuffer ()
1) First call processMultibulkBuffer, parse out the contents of the receive buffer according to the protocol format, and create a string object robject for each parameter

// The main processing is as follows
ok = string2ll (c-> querybuf + 1, newline- (c-> querybuf + 1), & ll); // 1, first parse out the number of parameters and convert to numbers
c-> multibulklen = ll; // Assign the number of parameters to multibulklen
c-> argv = zmalloc (sizeof (robj *) * c-> multibulklen); // 2, allocate memory for objects
c-> argv [c-> argc ++] = createStringObject (c-> querybuf + pos, c-> bulklen); // 3, create ll objects in sequence
For this example, ll = 3, and eventually 3 string objects will be generated, the contents of the string are "set", "key", "value".
C-> argv is of type robj ** argv;, so it can be regarded as an array, and each item in the array points to a robj.
As mentioned in the previous chapter, when the string length is less than 39 bytes, embstr encoding will be used to organize the data, so the final content of c-> argv is as follows:


2) Call processCommand to process the command
First search for the command. When the command does not exist or the number of parameters is incorrect, the error is returned directly.
Then a series of judgments will be made in the middle
Finally call call () to process the command
If the command in this example is set, you will find the set command in redisCommandTable, and then execute the function setCommand ()

int processCommand (redisClient * c) {
    c-> cmd = c-> lastcmd = lookupCommand (c-> argv [0]-> ptr);
    if (! c-> cmd) {// No command found
        return REDIS_OK;
    } else if ((c-> cmd-> arity> 0 && c-> cmd-> arity! = c-> argc) ||
               (c-> argc <-c-> cmd-> arity)) {// Wrong number of command parameters
        return REDIS_OK;
    }
    if (c-> flags & REDIS_MULTI &&
        c-> cmd-> proc! = execCommand && c-> cmd-> proc! = discardCommand &&
        c-> cmd-> proc! = multiCommand && c-> cmd-> proc! = watchCommand)
    {
        queueMultiCommand (c);
        addReply (c, shared.queued);
    } else {
        call (c, REDIS_CALL_FULL); // Execute the command
    }
    return REDIS_OK;
}
3) Call setCommand () to execute the command
SetCommand () has been briefly introduced in the previous article. It should be noted that finally setCommand () will be executed
AddReply (c, ok_reply? Ok_reply: shared.ok); Return the result of the operation to the client


2.3, call addReply to return the result to the client
1) This function will call prepareClientToWrite () and first save the result to be returned to the client to the buffer according to a certain format
2) Then call aeCreateFileEvent (server.el, c-> fd, AE_WRITABLE, sendReplyToClient, c) to register the write event on the connection fd.
In this way, the next time the server executes the aeMain function, it will detect that a write event has occurred and will call the sendReplyToClient () function.
3) In the sendReplyToClient () function, the write system call is called to return the result to the client through the socket.

Then the entire set key value command is executed



The source code cited in this article is all from Redis 3.0.7

Redis learning reference materials:
https://github.com/huangz1990/redis-3.0-annotated
Redis Design and Implementation (Second Edition)

Redis study notes (12) --- server basic process
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.