Redis event

The Redis server is an event driver, and the server needs to handle two types of events:

  1. File events: Redis servers connect to clients (or other Reids servers) through sockets, and file events are abstractions of socket operations by the server. Communication between a server and a client (or other Reids server) generates file events, which the server listens for and processes to complete a series of network communication operations.

  2. Time events: The Redis server abstracts actions (such as the serverCron function) that need to be performed at a given point in time.

This chapter introduces file events and time events, explains how they are used in Redis servers, how they are implemented, and the apis used to handle these events.

File events

Redis developed its own network event handler based on the Reactor pattern: This handler is called the File Event Handler:

  1. The file event handler uses the I/O multiplexing program to listen for multiple sockets at the same time and associate the socket with different event handlers based on the task it is currently performing.

  2. When the socket being listened to is ready to perform accept, read, write, close, etc., file events corresponding to the operation are generated, and the file event handler invokes the event handler associated with the socket to handle these events.

Although the file event handler runs in a single-threaded manner, by using I/O multiplexing programs to listen for multiple sockets, the file event handler implements a high-performance network communication model and interconnects well with other modules in the Redis server that also run in a single-threaded manner. This keeps the single-threaded design inside Redis simple.

File event handler composition

The following figure shows the four parts of a file event handler: socket, I/O multiplexer, file time dispatcher, and event handler.

File events are abstractions of socket operations! A file event is generated whenever a socket is ready to accept, write, read, close, and so on. Because a server typically connects to multiple sockets, multiple file events can occur concurrently.

The I/O multiplexer listens for multiple sockets and passes those sockets that generated the event to the file event dispatcher.

Although multiple file events may occur concurrently, I/O multiplexers always put all event-generating sockets into a queue, and then send sockets to the file event dispatler in a sequential, synchronously, one-socket at a time manner. After the event generated by the previous socket has been processed (the socket has been executed for the event handler associated with the event). The I/O multiplexer continues to deliver the next socket to the file event dispatcher. As shown below:

The file event dispatcher receives a socket from an I/O multiplexer and invokes the appropriate event handler based on the type of event generated by the socket.

The server associates different event handlers with sockets that perform different tasks. These handlers are functions that define the actions that the server should perform at a given time.

I/O multiplexing program implementation

All of the I/O multiplexing functionality in Redis is implemented by wrapping the common I/O multiplexing libraries such as SELECT, epoll, EVport, and KQueue. Each I/O multiplexing library corresponds to a separate file in the Redis source code. Such as ae_select.c, ae_epoll.c, ae_kqueue.c, and so on.

Because Redis implements the same API for each I/O multiplexing function library, the underlying implementation of the I/O multiplexing program is interchangeable, as shown in the following figure:

Redis uses the #include macro in the I/O multiplexing program implementation source code to define the corresponding rules, the program will automatically select the system at compile time the highest performance I/O multiplexing function library as the underlying IMPLEMENTATION of Reids I/O multiplexing program:

/* Include the best multiplexing layer supported by this system.
 * The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
    #ifdef HAVE_EPOLL
    #include "ae_epoll.c"
    #else
        #ifdef HAVE_KQUEUE
        #include "ae_kqueue.c"
        #else
        #include "ae_select.c"
        #endif
    #endif
#endif
Copy the code

Types of events

I/O multiplexers can listen for ae. H /AE_READABLE and AE. H /AE_WRITABLE events for multiple sockets. The mapping between these two types of events and socket operations is as follows:

  1. The socket generates an AE_READABLE event when it becomes readable (the client performs a write operation on the socket, or a close operation) or when a new acceptable socket appears (the client performs a connect operation on the server’s listening socket).

  2. When a socket becomes writable (the client performs the read operation on the socket), the socket generates an AE_WRITABLE event.

The I/O multiplexer allows the server to listen for both AE_READABLE and AE_WRITABLE events for sockets. If a socket produces both, the file event dispatcher will handle AE_READABLE events first. The AE_WRITABLE event will not be processed until after the AE_READABLE event is processed.

That is, if a socket is both readable and writable, the server will read the socket first and write the socket later.

API

The Ae. c/aeCreateFileEvent function takes a socket descriptor, an event type, and an event handler as parameters, adds a given event for a given socket to the listening scope of the I/O multiplexer, and associates the event with the event handler.

The Ae.c /aeDeleteFileEvent function takes a socket descriptor and a listener event type as arguments to let the I/O multiplexer unlisten for a given event on a given socket and unassociate the event with the event handler.

The ae.c/aeGetFileEvents function takes a socket descriptor and returns the type of event on which the socket is being listened:

  1. If no events are being listened for on the socket, the function returns AE_NONE.

  2. If the socket’s read event is being listened for, the function returns AE_READABLE.

  3. If socket write events are being listened for, the function returns AE_WRITABLE.

  4. If the socket read and write events are being listening, then the function returns AE_READABLE | AE_WRITABLE.

The ae.c/aeWait function takes a socket descriptor, an event type, and a millisecond as arguments, blocks and waits for an event of the given type for a socket for a given amount of time, and returns when the event is successfully generated or after waiting for a timeout.

The ae.c/aeApiPoll function takes a sys/time.h/struct timeval as an argument and blocks and waits for file events to occur on all sockets that are set to listen by aeCreateFileEvent for a specified time. The function returns when at least one event has occurred, or after waiting for a timeout.

The ae.c/aeProcessEvents function is a file event dispatcher that calls the aeApiPoll function to wait for events to occur, then iterates over all generated events and calls the appropriate event handler to handle them.

The Ae.c /aeGetApiName function returns the name of the I/O multiplexer library used by the underlying I/O multiplexer: returning “epoll” means the underlying epoll library, returning “SELECT” means the underlying SELECT library, and so on.

Handler for file events

Redis has written multiple handlers for file events, each of which is used to meet different network communication requirements, such as:

  1. In order to answer each client that connects to the server, the server associates the connection reply handler with the listening socket.

  2. To receive a command request from a client, the server associates the command request handler with the client socket.

  3. To return the execution result of a command to the client, the server associates the command reply handler with the client socket.

  4. When replication is performed on both master and slave servers, both master and slave servers need to associate replication handlers written specifically for replication functionality.

Among these event handlers, the server most commonly uses connection response handlers, command request handlers, and command reply handlers to communicate with the client.

Connection response processor

Networking. C/acceptTcpHandler function is Redis connection response processor, the CPU is used to connect to the server listening socket to reply the client, specific implementation for the sys/socket. H/the accept function of packaging.

When the Redis server is initialized, the program associates this connection reply handler with AE_READABLE events for the server to listen for sockets. When a client connects to the server to listen for sockets using sys/socket.h/connect, The socket will generate an AE_READABLE event that will cause the connection reply handler to execute and perform the corresponding socket reply operation, as shown in the figure below.

Command request handler

Networking. C/readQueryFromClient function is Redis command request processor, the CPU is responsible for the read from the socket client sends the command request content, concrete implementation for unistd. H/read function of packaging.

When a client successfully connects to the server through the connection reply handler, the server associates the AE_READABLE event for the client socket with the command request handler. When the client sends a command request to the server, the socket generates AE_READABLE events. The throw command asks the processor to execute and perform the corresponding socket read, as shown below.

The server will continue to request the handler for the AE_READABLE event associated command for the client socket the entire time the client connects to the server.

Command reply handler

Networking. C/reply sendReplyToClient function is Redis command processor, the CPU is responsible for the server to perform the command response resulting from the command through a socket returned to the client, the specific implementation for unistd. H/write function of packaging.

When the server has a command reply that needs to be sent to the client, the server associates the AE_WRITABLE event of the client socket with the command reply processor. When the client is ready to receive the command reply from the server, the AE_WRITABLE event will be generated, causing the command reply processor to execute. And perform the corresponding socket write operation, as shown in the figure below.When the command reply has been sent, the server disassociates the command reply handler from the AE_WRITABLE event of the client socket.

A complete example of a client-server connection event

Let’s trace the entire process of a Redis client connecting to a server and sending commands to see what events are generated and how they are handled.

Assuming that a Redis server is running, the AE_READABLE event for that server’s listening socket should be listening, and the handler for that event is the connection reply handler.

If a Redis client is making a connection to the server at this point, the listening socket will generate an AE_READABLE event that triggers the connection reply handler to execute: The processor responds to the client’s connection request, then creates the client socket, along with the client state, and associates the AE_READABLE event for the client socket with the command request handler so that the client can send command requests to the master server.

Later, if the client sends a command request to the master server, the client socket will generate an AE_READABLE event that will trigger the command request processor, which will read the contents of the client’s command and then pass it on to the relevant program to execute.

Executing the command produces the corresponding command reply. To pass the command reply back to the client, the server associates the AE_WRITABLE event of the client socket with the command reply handler: When a client attempts to read a command reply, the client socket generates an AE_WRITABLE event, which triggers the command reply handler to execute. When the command reply handler writes all the command reply to the socket, The server disassociates the AE_WRITABLE event of the client socket from the command reply handler.

The following figure summarizes the entire communication process described above and the event handlers used to communicate.

Time event

Redis time events fall into the following two categories:

  1. Timed event: To cause a program to execute once after a specified time. For example, let program X execute once 30 milliseconds after the current time.

  2. Periodic events: To cause a program to execute at specified intervals. For example, let program Y execute every 30 milliseconds.

A time event mainly consists of the following three attributes:

  1. Id: globally unique ID (identifier) created by the server for the time event. ID numbers increase in ascending order, with the new event ID being larger than the old event ID.

  2. When: A MILLISECond precision UNIX timestamp that records the arrival time of a time event.

  3. TimeProc: Time event handler, a function. When the time event arrives, the server invokes the appropriate handler to handle the event.

Whether a time event is a timed or periodic event depends on the value returned by the time event handler:

  1. If the event handler returns ae.h/AE_NOMORE, the event is a timed event: the event is deleted after it is reached once and is never reached again.

  2. If the event handler returns a non AE_NOMORE integer value, then the event for the periodic time: after a time of arrival, the server will, according to the value returned by an event handler to update the when time events attributes, let this incident again after a period of time to arrive, and in this way has been updated and run down. For example, if a time event handler returns an integer value of 30, the server should update the time event so that it arrives again 30 milliseconds later

The current version of Redis uses only periodic events, not timed events.

implementation

The server keeps all time events in an unordered linked list, and whenever the time event executor runs, it walks through the list, looking for all time events that have arrived, and invokes the appropriate event handler.

The following figure shows an example of a linked list that holds temporal events. The linked list contains three different temporal events: since new temporal events are always inserted into the header of the list, the three temporal events are sorted in reverse order by ID, with the ID of the header event being 3, the ID of the middle event being 2, and the ID of the tail event being 1.

Notice that we say that the linked list that holds time events isUnordered list, not that the list is not sorted by ID, but rather that the list is not sorted by the size of the WHEN attribute. Because the list is not sorted by the WHEN attribute, when the time event executor runs, it must iterate through all the time events in the list to ensure that all arrived time events in the server are processed.

Unordered linked lists do not affect the performance of time event handlers. In the current version, the Redis server uses only one time event serverCron in normal mode, and only two time events in Benchmark mode. In this case, the server is almost degrading the unordered list to a pointer, so using the unordered list to store temporal events does not affect the performance of event execution.

API

Ae. C/aeCreateTimeEvent function: Take as arguments a milliseconds and a proc event handler to add a new event to the server that will arrive after the current milliseconds. The handler is proc.

For example, if the server is currently saving time events as shown in the first picture below. So when the program calls aeCreateTimeEvent at time 1385877599980 (20 milliseconds before zero on December 1, 2013) with parameters of 50 ms and handler_3 processor, the server will create a time event with ID 3. The time events saved by the server will look like the second picture below.

The ae.c/aeDeleteFileEvent function: the number takes a time event ID as an argument and then removes the time event corresponding to that ID from the server.

For example, if the server currently saves time events as shown in the second image above, then when the program calls aeDeleteFileEvent (3), the server saves time events as shown in the first image above.

Ae. : c/aeSearchNearestTimer function returns the current time the most close to the distance from time to time.

For example, if the current time is 1385877599980 (20 milliseconds before midnight on December 1, 2013) and the server is currently saving the time event as shown in the second image above, calling the aeSearchNearestTimer function will return the event with ID 2.

Ae. C /processTimeEvents function: the number is the executor of time events. This function iterates through all the time events that have arrived and calls the handlers for those events. Arrived means that the UNIX timestamp recorded by the when attribute of the time event is equal to or less than the current UNIX timestamp.

For example, if the server saves a time event as shown in the second image above and the current time is 1385877600010 (10 milliseconds after midnight on December 1, 2013), the processTimeEvents function will process the time events with IDS 2 and 1 in the figure, Because the arrival time of these two events is greater than or equal to 1385877600010.

The definition of the processTimeEvents function can be described in pseudocode:

Def processTimeEvents(): # for time_event in all_time_event(): If time_event. When <= unix_ts_now(): TimeProc () # If this is a timed event if retval == AE_NOMORE: Delete_time_event_from_server (time_event) # If this is a periodic event else: Update_when (time_event, retval) when(time_event, retval)Copy the code

Time event application example: the serverCron function

A continuously running Redis server needs to periodically check and adjust its resources and state to ensure long-term and stable operation of the server. These periodic operations are performed by the Redis. C /serverCron function.

  1. Update server statistics, such as time, memory usage, and database usage.

  2. Clean up stale key-value pairs in the database.

  3. Close and clean up clients with failed connections.

  4. An AOF or RDB persistence operation was attempted.

  5. If the server is the primary, the secondary server is synchronized periodically.

  6. If the cluster is in cluster mode, periodically synchronize and connect the cluster

  7. The Redis server runs the serverCron function on a periodic event basis, and the serverCron is executed at intervals during the server run until the server is shut down.

In Redis2.6, the server defaults to serverCron running 10 times per second, with an average interval of 100 milliseconds.

Starting with Redis2.8, you can modify the Hz option to adjust the number of times the serverCron executes per second. For details, see the description of the Hz option in the example configuration file redis.conf.

Scheduling and execution of events

Because there are both file and time event types in the server, the server must schedule for both events, deciding when to handle file events, when to handle time events, and how much time to handle them.

The scheduler and execution of events is handled by the ae.c/aeProcessEvents function: Note that the processFileEvents function was not mentioned in section 1.1 when the file event API was introduced, because it does not exist. In practice, the code that handles the generated file events is written directly inside the aeProcessEvents function, but the processFileEvents function has been invented here for convenience

def aeProcessEvents(): Time_event = aeSearchNearestTimer() # remaind_ms = time_event.when - Unix_ts_now () # remaind_ms = 0 if remaind_ms < 0: Timeval = create_timeval_with_ms(remaind_ms) # block and wait for file events to occur, If the value of remaind_ms is 0, then aeApiPoll will return immediately after the call, AeApiPoll (timeVal) # process all generated file events processFileEvents() # Process all arrived time eventsCopy the code

Putting the aeProcessEvents function in a loop, along with the initialization and cleanup functions, makes up the Redis server’s main function. Here is the pseudo-code representation of this function:

Def main(): # init server init_server() # process events until the server is shutdown while server_is_not_shutdown(): AeProcessEvents () # Shut down the server and perform clean_server()Copy the code

From the point of view of event handling, the running flow of the Redis server can be represented as a flow chart:

Here are the scheduling and execution rules for events:

  1. The maximum blocking time of the aeApiPoll function is determined by the time event whose arrival time is closest to the current time. This method can not only avoid frequent polling (busy wait) by the server for time events, but also ensure that the aeApiPoll function will not be blocked for too long.

  2. Because file events are random, if no time event arrives after waiting and processing a file event, the server will wait and process the file event again. As the file event continues to execute, the time gradually approaches the arrival time set by the time event, and finally reaches the arrival time, at which point the server can start processing the arrival time event.

  3. Handling of files and time events are synchronized, orderly and the enforcement of atoms, the server will not interruptions event processing, also can’t to preempt the event, therefore, whether it’s file processor of events, or events of processor time, they will try to reduce the program block time, and volunteer executive power in times of need, Thus reducing the likelihood of causing incident hunger. For example, when a command reply processor writes a command reply to a client socket, if the number of bytes written exceeds a preset constant, the command reply processor breaks out of the write loop, saving the rest of the data for another time. In addition, time events can push very time-consuming persistence operations into child threads or processes.

  4. Because time events are executed after file events and there is no preemption between events, the actual processing time of time events is usually a little later than the arrival time of time events.

The following table records a complete event scheduling and execution process:

The event execution process recorded in the above table highlights rules 2, 3, and 4 of the event scheduling rules listed above:

  1. Because the time event has not yet arrived, the server has waited and processed two file events before processing the time event.

  2. Because there is no preemption during event processing, the actual time to process the event is 30 milliseconds behind the scheduled time of 100 milliseconds.

conclusion

  1. Redis server is an event driver. The events handled by the server are classified into time events and file events.

  2. File event handler is a network communication program based on Reactor model.

  3. File events are an abstraction of socket operations: they are generated each time the socket becomes acceptable, writable, or readable.

  4. File events are classified into AE_READABLE events and AE_WRITABLE events.

  5. Time events are classified into periodic events and periodic events. Periodic events arrive at a specified time only, while periodic events arrive at a specified interval.

  6. The server normally only executes the serverCron function one time event, and this event is periodic.

  7. There is a cooperative relationship between file events and time events, and the server takes turns handling both events without preemption.

  8. The actual processing time of time events is usually a little later than the set arrival time