Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the REdis command processing process?

2025-04-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the relevant knowledge of "what is the processing process of REdis command processing process". In the operation of actual cases, many people will encounter this dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Analysis version: REdis-5.0.4.

The REdis command processing flow can be broken down into three separate processes (excluding replication and persistence):

Accept connection request process

Receive the request data and process the request flow, in which the processing result is not sent to Client, but the result data is written to the response buffer and will be sent by the response request process

Response request process.

The above three processes are asynchronized and have no direct connection. What they all have in common is that they are triggered by REdis's simple event-driven (AE,A simple event-driven). For Linux, it is actually the packaging of epoll, for the packaging of macOS is evport, for the packaging of FreeBSD to kqueue, for others, it is the packaging of select.

You can think of ae.h/ae.c as an abstract base class, and ae_epoll.c, ae_select.c, ae_evport.c, and ae_kqueue.c as concrete implementations of ea. From an object-oriented point of view, it is roughly shown in the following figure:

As you can see from the figure above, when there is no data, the process blocks the timeout directly at the function aeApiPoll (which is actually epoll_wait for epoll).

If a connection request comes in, or if a connection sends data, or if the response data has not yet been sent (the connection becomes writable), aeApiPoll will immediately return from the blocking state.

Notice that only fd is stuffed into epoll, not client or aeFileEvent into epoll. So when a connection is activated (for example, there is data to receive), you need to find the aeFileEvent through fd, and client is assigned to aeFileEvent's clientData when the aeFileEvent is created, so you only need to find the aeFileEvent.

The global object server (type redisServer, defined in server.h) maintains a global aeEventLoop presence, then aeEventLoop maintains an array of aeFileEvent, and the array index of aeFileEvent is fd, so it is easy to find the corresponding aeFileEvent through fd.

The reason why aeFileEvent is not injected directly into epoll is to unify event-driven, such as select does not support. When the process starts to execute initServer, aeCreateEventLoop is called to initialize the array, which is larger than the value specified by the configuration item maxclients. This takes advantage of the recycling of fd as a kernel resource of the operating system.

1. Accept connection request process

After accepting a connection, create a client object for the connection and register the client with epoll with the registration event EPLLIN (corresponding to ea's AE_READABLE).

Corresponding pseudo code:

Int main () {/ / the abbreviation void aeMain () {while (! eventLoop- > stop) {/ / "ae" is "A simple event-driven") {/ / the response starts with beforesleep and / / the unfinished part goes to aeApiPoll. If (eventLoop- > beforesleep! = NULL) eventLoop- > beforesleep (eventLoop) / / aeProcessEvents handles various events, including: / / 1) accept connection requests, create a client for each connection / / 2) receive request data, and process requests / / 3) send response data / / 4) handle all kinds of scheduled events (call processTimeEvents) int aeProcessEvents () {/ / aeApiPoll is actually aeApiPoll () such as epoll or select AcceptTcpHandler (int fd) {/ / fd is a listen socket / / anetTcpAccept underlying call is accept int cfd = anetTcpAccept (fd); acceptCommonHandler (cfd) {/ / createClient adds c to server.clients, and / / server.clients is a link. Client * c = createClient (cfd) {aeCreateFileEvent (server.el,fd,AE_READABLE, readQueryFromClient, c) {/ / mask value is AE_READABLE (corresponding to epoll's EPOLLIN), / / epoll_ctl is actually called for epoll. AeApiAddEvent (eventLoop,fd,mask);}

two。 Receive request data and process request flow

This block calls the handler of the corresponding command, such as the handler of the SET command, the handler of the setCommand,GET command, and the handler of the setCommand,GET command. The command processor modifies in-memory data.

The result of the processing is written to the response buffer, but is not immediately sent to client. The result of the processing is also written to the AOF buffer if AOF is turned on. And write commands to the copy backlog buffer, if open or necessary. The command is also written to the buffer of slaves, if necessary.

The response request is made in a separate process, which does not send the response directly to the client.

Corresponding pseudo code

/ / does not include the response command, / / response and receive processing are two separate processes. Int main () / / server.c:4003 {/ / the abbreviation void aeMain () / / ae.c:496 {while (! eventLoop- > stop) {/ / send the response in beforesleep first, / / if the response is not finished in beforesleep (for example, the amount of data of the response is too large), / / the subsequent transmission will be triggered by aeApiPoll. If (eventLoop- > beforesleep! = NULL) eventLoop- > beforesleep (eventLoop); int aeProcessEvents () / ae.c:358 {/ / aeApiPoll is actually aeApiPoll () such as epoll or select; / / readQueryFromClient is a callback function, / / register when creating client: / / client * createClient (int fd) {/ / aeCreateFileEvent (/ / server.el, fd,// AE_READABLE,// readQueryFromClient, c) / /} void readQueryFromClient () / / networking.c:1494 {/ / the size of the data sent by calling read / / client cannot exceed the value specified by the configuration item client-query-buffer-limit. / / the default size is 1G, which is enough to cover most of the scenes. / / if the size is exceeded, you can see the WARNING log: / / Closing client that reached max query buffer length / / in practice, it is generally much less than 1G, so it is possible to lower this value to increase the protection of REdis. Int nread = read (fd, c-> querybuf, readlen); int processCommand (client*) / / networking.c:2543 {redisCommand* lookupCommand (name) {/ / REdis all commands are stored / / in command table of struct redisServer: / / struct redisServer {/ / dict * commands; / / Command table / /} / / think of redisCommand as a C++ abstract base, / / this abstraction basically defines a pure virtual function proc: / / typedef void redisCommandProc (client * c); / / struct redisCommand {/ / redisCommandProc * proc; / /}; and each member of the command table is the implementation of redisCommand. Return dictFetchValue (commands,name);} void call (client*,flags) / / server.c:2414 {/ / callback specific command processing: / / if it is a SET command, / / the function setCommand in t_string.c is actually called / / if it is a DEL command, / / the function delCommand in db.c is actually called. RedisCommand::proc (client*); void propagate (redisCommand*) / / server.c:2315 {/ / data is written to the AOF file feedAppendOnlyFile () / / aof.c:555 / / data is copied to all Slaves void replicationFeedSlaves (slaves) / / replication.c:173 {/ / data is written to the copy backlog (Backlog) buffer, / / Note that the backlog buffer is a circular buffer, / / if full, write from scratch / / the size of the circular buffer, / / the configuration item repl-backlog-size determines feedReplicationBacklog () / / replication.c:126} / / take the GET command as the column: / / the list here is actually server.clients_pending_write//, so all the client to be responded are added to the server.clients_pending_write linked list (which can be regarded as a queue) / / struct redisServer server / / Server global state#0 listAddNodeHead (list=0x7fe88bc0f210, value=0x7fe88bc64ec0) at adlist.c:92// not all commands require WriteHandler,//, so some don't call clientInstallWriteHandler. # 1 in clientInstallWriteHandler (c=0x7fe88bc64ec0) at networking.c:185#2 in prepareClientToWrite (c=0x7fe88bc64ec0) at networking.c:228#3 in addReplyString (c=0x7fe88bc64ec0, s=0x7ffdfc2e70c0 "$855\ r\ n", len=6) at networking.c:338#4 in addReplyLongLongWithPrefix (c=0x7fe88bc64ec0, ll=855, prefix=36'$') at networking.c:515#5 in addReplyBulkLen (c=0x7fe88bc64ec0, obj=0x7fe889312840) at networking.c:557#6 in addReplyBulk (c=0x7fe88bc64ec0 Obj=0x7fe889312840) at networking.c:562#7 in getGenericCommand (c=0x7fe88bc64ec0) at t_string.c:167#8 in getCommand (c=0x7fe88bc64ec0) at t_string.c:173#9 in call (c=0x7fe88bc64ec0, flags=15) at server.c:2437#10 in processCommand (c=0x7fe88bc64ec0) at server.c:2729#11 in processInputBuffer (c=0x7fe88bc64ec0) at networking.c:1451#12 in processInputBufferAndReplicate (c=0x7fe88bc64ec0) at networking.c:1486#13 in readQueryFromClient (el=0x7fe88bc30050, fd=8, privdata=0x7fe88bc64ec0, mask=1) at networking.c:1568#14 in aeProcessEvents (eventLoop=0x7fe88bc30050 Flags=11) at ae.c:443#15 in aeMain (eventLoop=0x7fe88bc30050) at ae.c:501#16 in main (argc=2, argv=0x7ffdfc2e75b8) at server.c:4197

3. Response request process

For every responsive command, its response is always done first in beforesleep, but if it fails to be sent at one time, it will be handed over to sendReplyToClient for subsequent asynchronous processing (in the case of epoll, by registering the EPOLLOUT event of epoll).

Corresponding pseudo code:

/ / response and receive processing are two separate processes. Int main () {/ / "ae" is the abbreviation void aeMain () {while (! eventLoop- > stop) {/ / call eventLoop- > beforesleep (eventLoop) / / but what is actually called is beforeSleep in server.c: void beforeSleep (struct aeEventLoop*) {int handleClientsWithPendingWrites () {/ / REdis receives and processes / / the command flow sets clients_pending_write, and / / clients_pending_write is actually a queue link. / / after processing a command, call clientInstallWriteHandler / / to add the current client to the clients_pending_write. / / but some commands do not need to respond, so there is no such action. ListRewind (server.clients_pending_write,&li); while ((ln = listNext (& li)) {int writeToClient (int fd,client* c) {write (fd,c- > buf); / / if all are sent, / / call aeDeleteFileEvent / / to remove fd from epoll. If (! clientHasPendingReplies (c)) {aeDeleteFileEvent (server.el, c-> fd, AE_WRITABLE) / / remove EPOLLOUT from epoll} / / if a writeToClient call is not finished, / / register fd with epoll if (clientHasPendingReplies (c)) {/ / the following action is to set EPOLLOUT int ae_flags = AE_WRITABLE of epoll / / add EPOLLOUT to epoll aeCreateFileEvent (server.el, c-> fd, ae_flags, sendReplyToClient, c);} / / REdis receives and processes a command flow aeProcessEvents () This is the end of the content of "what is the REdis command processing process". Thank you for your reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report