真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

REdis命令處理流程處理過(guò)程是什么

本篇內(nèi)容介紹了“redis命令處理流程處理過(guò)程是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

坡頭ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書未來(lái)市場(chǎng)廣闊!成為成都創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18980820575(備注:SSL證書合作)期待與您的合作!

分析版本:REdis-5.0.4。

REdis命令處理流程可分解成三個(gè)獨(dú)立的流程(不包括復(fù)制和持久化):

  • 接受連接請(qǐng)求流程;

  • 接收請(qǐng)求數(shù)據(jù)和處理請(qǐng)求流程,在這個(gè)過(guò)程并不會(huì)發(fā)送處理結(jié)果給Client,而只是將結(jié)果數(shù)據(jù)寫入響應(yīng)緩沖,將由響應(yīng)請(qǐng)求流程來(lái)發(fā)送;

  • 響應(yīng)請(qǐng)求流程。

上述三個(gè)流程均是異步化的,并且沒有直接的聯(lián)系。它們的共同點(diǎn)均是通過(guò)REdis的簡(jiǎn)單事件驅(qū)動(dòng)(AE,A simple event-driven)觸發(fā),對(duì)于Linux實(shí)際是epoll的包裝,對(duì)于macOS為evport的包裝,對(duì)于FreeBSD對(duì)kqueue的包裝,對(duì)于其它則是select的包裝。

可以把a(bǔ)e.h/ae.c看成是抽象基類,而ae_epoll.c、ae_select.c、ae_evport.c、ae_kqueue.c看成是ea的具體實(shí)現(xiàn),以面向?qū)ο髞?lái)看,大致如下圖所示:

REdis命令處理流程處理過(guò)程是什么

從上圖可以看出,當(dāng)沒有任何數(shù)據(jù)時(shí),進(jìn)程將阻塞在函數(shù)aeApiPoll(對(duì)于epoll實(shí)際為epoll_wait)處直接超時(shí)。
如果有連接請(qǐng)求進(jìn)來(lái),或者有連接發(fā)送數(shù)據(jù)過(guò)來(lái),或者有響應(yīng)數(shù)據(jù)還未發(fā)送完成(連接變成可寫),aeApiPoll均會(huì)立即從阻塞狀態(tài)返回。

注意,只有fd被塞進(jìn)了epoll,并沒有將client或aeFileEvent塞入epoll。因此當(dāng)一個(gè)連接被激活(比如有數(shù)據(jù)需要接收)時(shí),需要通過(guò)fd來(lái)查找到aeFileEvent,而client因?yàn)樵趧?chuàng)建aeFileEvent時(shí)就被賦值給了aeFileEvent的clientData,因此只需要找到aeFileEvent即可。

全局對(duì)象server(類型為redisServer,定義在server.h中)維護(hù)了一個(gè)全局的aeEventLoop在,則aeEventLoop維護(hù)了一個(gè)aeFileEvent數(shù)組,并且aeFileEvent的數(shù)組下標(biāo)為fd,因此很容易通過(guò)fd找到對(duì)應(yīng)的aeFileEvent。

之所以沒有將aeFileEvent直接注入到epoll,是為了統(tǒng)一事件驅(qū)動(dòng),比如select就不支持。在進(jìn)程啟動(dòng)執(zhí)行initServer時(shí),會(huì)調(diào)用aeCreateEventLoop初始化該數(shù)組,數(shù)組大小大于配置項(xiàng)maxclients指定的值(額外加128),這利用了fd作為操作系統(tǒng)內(nèi)核資源是循環(huán)利用的特性。

1. 接受連接請(qǐng)求流程
接受一個(gè)連接后,為該連接創(chuàng)建一個(gè)client對(duì)象,并將該client注冊(cè)到epoll中,注冊(cè)事件為EPLLIN(對(duì)應(yīng)于ea的AE_READABLE)。

REdis命令處理流程處理過(guò)程是什么

對(duì)應(yīng)的偽代碼:

int main()
{

  // “ae”為“A simple event-driven”的縮寫

  void aeMain()
{    while (!eventLoop->stop)    {      // 響應(yīng)從beforesleep開始,      // 未完成部分才會(huì)走到aeApiPoll。      if (eventLoop->beforesleep != NULL)        eventLoop->beforesleep(eventLoop);                  // aeProcessEvents處理各種事件,包括:      // 1) 接受連接請(qǐng)求,為每個(gè)連接創(chuàng)建一個(gè)client      // 2) 接收請(qǐng)求數(shù)據(jù),和處理請(qǐng)求      // 3) 發(fā)送響應(yīng)數(shù)據(jù)      // 4) 處理各類定時(shí)事件(調(diào)用processTimeEvents)      int aeProcessEvents()
{        // aeApiPoll實(shí)為epoll或select等        aeApiPoll();              acceptTcpHandler(int fd)        {          // fd為listen套接字          // anetTcpAccept底層調(diào)用的是accept          int cfd = anetTcpAccept(fd);                    acceptCommonHandler(cfd)          {            // createClient會(huì)將c添加server.clients中,            // server.clients是一個(gè)鏈接。            client *c = createClient(cfd)            {              aeCreateFileEvent(                server.el,fd,AE_READABLE,                readQueryFromClient, c)              {                // mask值為AE_READABLE(對(duì)應(yīng)于epoll的EPOLLIN),                // 對(duì)于epoll實(shí)際調(diào)用的是epoll_ctl。                aeApiAddEvent(eventLoop,fd,mask);              }            }          }        }      }    }
  }

}

2. 接收請(qǐng)求數(shù)據(jù)和處理請(qǐng)求流程
這一塊會(huì)調(diào)用相應(yīng)命令的處理函數(shù),比如SET命令的處理函數(shù)setCommand,GET命令的處理函數(shù)getCommand。命令處理函數(shù)會(huì)修改內(nèi)存數(shù)據(jù)。
并將處理的結(jié)果寫入響應(yīng)緩沖區(qū),但并不立即發(fā)送給client。同時(shí)也會(huì)將處理結(jié)果寫入AOF緩沖區(qū),如果開啟了AOF。以及將命令寫入到復(fù)制積壓緩沖區(qū),如果有開啟或有需要。還會(huì)將命令寫入到slaves的緩沖區(qū),如果需要。
響應(yīng)請(qǐng)求在另一獨(dú)立的流程中進(jìn)行,本流程并不直接發(fā)送響應(yīng)給client。

REdis命令處理流程處理過(guò)程是什么

對(duì)應(yīng)的偽代碼

// 不包括響應(yīng)命令,// 響應(yīng)和接收處理是分開的兩個(gè)過(guò)程。

int main() // server.c:4003{  // “ae”為“A simple event-driven”的縮寫

  void aeMain() // ae.c:496

  {while (!eventLoop->stop)

    {      // 發(fā)送響應(yīng)先在beforesleep中進(jìn)行,      // 如果在beforesleep中沒有發(fā)送完(比如響應(yīng)的數(shù)據(jù)量過(guò)大),      // 則后續(xù)的發(fā)送會(huì)由aeApiPoll觸發(fā)。      if (eventLoop->beforesleep != NULL)

        eventLoop->beforesleep(eventLoop);

 

      int aeProcessEvents() // ae.c:358  {// aeApiPoll實(shí)為epoll或select等

        aeApiPoll(); // readQueryFromClient是個(gè)回調(diào)函數(shù),// 在創(chuàng)建client時(shí)注冊(cè):// client *createClient(int fd) {//   aeCreateFileEvent(//       server.el, fd,//       AE_READABLE,//       readQueryFromClient, c);// }

        

        void readQueryFromClient() // networking.c:1494{          // 這里調(diào)用read收數(shù)據(jù)          // client傳過(guò)來(lái)的數(shù)據(jù)大小不能超過(guò)配置項(xiàng)client-query-buffer-limit指定的值。          // 默認(rèn)大小為1G,足夠覆蓋大部場(chǎng)景。          // 如果超過(guò)大小,則可看到WARNING日志:          // Closing client that reached max query buffer length          // 實(shí)際中,一般遠(yuǎn)小于1G,所以可能將這個(gè)值調(diào)小一點(diǎn),以增加對(duì)REdis的保護(hù)。

          int nread = read(fd, c->querybuf, readlen);

          int processCommand(client*) // networking.c:2543  {

            redisCommand* lookupCommand(name)

            {              // REdis所有命令存儲(chǔ)              // 在struct redisServer的command表中:              // struct redisServer {              //    dict *commands; // Command table              // };              // 可將redisCommand看作一個(gè)C++抽象基本,              // 該抽象基本定義了純虛函數(shù)proc:              // typedef void redisCommandProc(client *c);              // struct redisCommand {              //   redisCommandProc *proc;              // };              // 而command表中的每一個(gè)成員則為redisCommand的實(shí)現(xiàn)。

              return dictFetchValue(commands,name);

            }

 

            void call(client*,flags) // server.c:2414{              // 回調(diào)具體的命令處理:              // 如果是SET命令,              // 實(shí)際調(diào)用的是t_string.c中的函數(shù)setCommand;              // 如果是DEL命令,              // 實(shí)際調(diào)用的是db.c中的函數(shù)delCommand。

              redisCommand::proc(client*);

 

              void propagate(redisCommand*) // server.c:2315  {// 數(shù)據(jù)寫入到AOF文件

                feedAppendOnlyFile(); // aof.c:555

 // 數(shù)據(jù)復(fù)制給所有Slaves

                void replicationFeedSlaves(slaves) // replication.c:173{                  // 數(shù)據(jù)寫入到復(fù)制積壓(Backlog)緩沖區(qū),                  // 注意積壓緩沖區(qū)是一個(gè)循環(huán)緩沖區(qū),                  // 如果滿了,則從頭覆蓋寫,                  // 循環(huán)緩沖區(qū)的大小,                  // 則配置項(xiàng)repl-backlog-size決定

                  feedReplicationBacklog(); // replication.c:126}

              }

            }

          }

        }

      }

    }

  }

} 

// 以GET命令為列:// 這里的list實(shí)際為server.clients_pending_write// 所以需響應(yīng)的client都添加到server.clients_pending_write鏈表中(可視為隊(duì)列)// struct redisServer server; // Server global state#0  listAddNodeHead (list=0x7fe88bc0f210, value=0x7fe88bc64ec0) at adlist.c:92// 并不是所有的命令都需要WriteHandler,// 因此有些并不會(huì)調(diào)用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. 響應(yīng)請(qǐng)求流程

對(duì)于每一個(gè)有響應(yīng)的命令,它的響應(yīng)總是首先在beforesleep中進(jìn)行,但如果一次沒能發(fā)送完成,則會(huì)交給sendReplyToClient后續(xù)異步處理(以epoll為例,通過(guò)注冊(cè)epoll的EPOLLOUT事件)。

REdis命令處理流程處理過(guò)程是什么

對(duì)應(yīng)的偽代碼:

// 響應(yīng)和接收處理是分開的兩個(gè)過(guò)程。

int main()

{

  // “ae”為“A simple event-driven”的縮寫

  void aeMain()

  {    while (!eventLoop->stop)    {      // 調(diào)用eventLoop->beforesleep(eventLoop);      // 但實(shí)際調(diào)用的是server.c中的beforeSleep:      void beforeSleep(struct aeEventLoop*)      {        int handleClientsWithPendingWrites()        {          // REdis接收和處理          // 命令流程會(huì)設(shè)置clients_pending_write,          // clients_pending_write實(shí)為一個(gè)隊(duì)列鏈接。          // 當(dāng)處理完一個(gè)命令后,調(diào)用clientInstallWriteHandler          // 將當(dāng)前client添加到clients_pending_write中。          // 但是有些命令并不需要響應(yīng),因此沒有這個(gè)動(dòng)作。          listRewind(server.clients_pending_write,&li);
           while((ln = listNext(&li)))          {            int writeToClient(int fd,client* c)            {              write(fd,c->buf);              // 如果全部發(fā)送完了,              // 則調(diào)用aeDeleteFileEvent              // 將fd從epoll中移除。              if (!clientHasPendingReplies(c))              {                aeDeleteFileEvent(                  server.el, c->fd, AE_WRITABLE); // 從epoll中刪除EPOLLOUT              }            }                        // 如果一次writeToClient調(diào)用沒有發(fā)完,            // 則將fd注冊(cè)到epoll            if (clientHasPendingReplies(c))            {              // 下列動(dòng)作是設(shè)置epoll的EPOLLOUT              int ae_flags = AE_WRITABLE; // 將EPOLLOUT添加到epoll中              aeCreateFileEvent(                server.el, c->fd, ae_flags,                sendReplyToClient, c);            }          }        }      }
       // REdis接收和處理一個(gè)命令流程      aeProcessEvents();    }
   }

}

“REdis命令處理流程處理過(guò)程是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!


文章名稱:REdis命令處理流程處理過(guò)程是什么
轉(zhuǎn)載來(lái)源:http://weahome.cn/article/pjoiei.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部