亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > Redis > 正文

Redis中事件驅動模型示例詳解

2020-03-17 12:34:37
字體:
來源:轉載
供稿:網友

前言

Redis 是一個事件驅動的內存數據庫,服務器需要處理兩種類型的事件。

  • 文件事件
  • 時間事件

下面就會介紹這兩種事件的實現原理。

文件事件

Redis 服務器通過 socket 實現與客戶端(或其他redis/224812.html">redis服務器)的交互,文件事件就是服務器對 socket 操作的抽象。 Redis 服務器,通過監聽這些 socket 產生的文件事件并處理這些事件,實現對客戶端調用的響應。

Reactor

Redis 基于 Reactor 模式開發了自己的事件處理器。

這里就先展開講一講 Reactor 模式??聪聢D:

redis事件驅動,redis,模型,事件模型

“I/O 多路復用模塊”會監聽多個 FD ,當這些FD產生,accept,read,write 或 close 的文件事件。會向“文件事件分發器(dispatcher)”傳送事件。

文件事件分發器(dispatcher)在收到事件之后,會根據事件的類型將事件分發給對應的 handler。

我們順著圖,從上到下的逐一講解 Redis 是怎么實現這個 Reactor 模型的。

I/O 多路復用模塊

Redis 的 I/O 多路復用模塊,其實是封裝了操作系統提供的 select,epoll,avport 和 kqueue 這些基礎函數。向上層提供了一個統一的接口,屏蔽了底層實現的細節。

一般而言 Redis 都是部署到 Linux 系統上,所以我們就看看使用 Redis 是怎么利用 linux 提供的 epoll 實現I/O 多路復用。

首先看看 epoll 提供的三個方法:

/* * 創建一個epoll的句柄,size用來告訴內核這個監聽的數目一共有多大 */int epoll_create(int size);/* * 可以理解為,增刪改 fd 需要監聽的事件 * epfd 是 epoll_create() 創建的句柄。 * op 表示 增刪改 * epoll_event 表示需要監聽的事件,Redis 只用到了可讀,可寫,錯誤,掛斷 四個狀態 */int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);/* * 可以理解為查詢符合條件的事件 * epfd 是 epoll_create() 創建的句柄。 * epoll_event 用來存放從內核得到事件的集合 * maxevents 獲取的最大事件數 * timeout 等待超時時間 */int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

再看 Redis 對文件事件,封裝epoll向上提供的接口:

/* * 事件狀態 */typedef struct aeApiState { // epoll_event 實例描述符 int epfd; // 事件槽 struct epoll_event *events;} aeApiState;/* * 創建一個新的 epoll  */static int aeApiCreate(aeEventLoop *eventLoop)/* * 調整事件槽的大小 */static int aeApiResize(aeEventLoop *eventLoop, int setsize)/* * 釋放 epoll 實例和事件槽 */static void aeApiFree(aeEventLoop *eventLoop)/* * 關聯給定事件到 fd */static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask)/* * 從 fd 中刪除給定事件 */static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask)/* * 獲取可執行事件 */static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp)

所以看看這個ae_peoll.c 如何對 epoll 進行封裝的:

  • aeApiCreate() 是對 epoll.epoll_create() 的封裝。
  • aeApiAddEvent()和aeApiDelEvent() 是對 epoll.epoll_ctl()的封裝。
  • aeApiPoll() 是對 epoll_wait()的封裝。

這樣 Redis 的利用 epoll 實現的 I/O 復用器就比較清晰了。

再往上一層次我們需要看看 ea.c 是怎么封裝的?

首先需要關注的是事件處理器的數據結構:

typedef struct aeFileEvent { // 監聽事件類型掩碼, // 值可以是 AE_READABLE 或 AE_WRITABLE , // 或者 AE_READABLE | AE_WRITABLE int mask; /* one of AE_(READABLE|WRITABLE) */ // 讀事件處理器 aeFileProc *rfileProc; // 寫事件處理器 aeFileProc *wfileProc; // 多路復用庫的私有數據 void *clientData;} aeFileEvent;

mask 就是可以理解為事件的類型。

除了使用 ae_peoll.c 提供的方法外,ae.c 還增加 “增刪查” 的幾個 API。

  • 增:aeCreateFileEvent
  • 刪:aeDeleteFileEvent
  • 查: 查包括兩個維度 aeGetFileEvents 獲取某個 fd 的監聽類型和aeWait等待某個fd 直到超時或者達到某個狀態。

事件分發器(dispatcher)

Redis 的事件分發器 ae.c/aeProcessEvents 不但處理文件事件還處理時間事件,所以這里只貼與文件分發相關的出部分代碼,dispather 根據 mask 調用不同的事件處理器。

//從 epoll 中獲關注的事件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) {  // rfired 確保讀/寫事件只能執行其中一個  rfired = 1;  fe->rfileProc(eventLoop,fd,fe->clientData,mask); } // 寫事件 if (fe->mask & mask & AE_WRITABLE) {  if (!rfired || fe->wfileProc != fe->rfileProc)   fe->wfileProc(eventLoop,fd,fe->clientData,mask); } processed++;}

可以看到這個分發器,根據 mask 的不同將事件分別分發給了讀事件和寫事件。

文件事件處理器的類型

Redis 有大量的事件處理器類型,我們就講解處理一個簡單命令涉及到的三個處理器:

  • acceptTcpHandler 連接應答處理器,負責處理連接相關的事件,當有client 連接到Redis的時候們就會產生 AE_READABLE 事件。引發它執行。
  • readQueryFromClinet 命令請求處理器,負責讀取通過 sokect 發送來的命令。
  • sendReplyToClient 命令回復處理器,當Redis處理完命令,就會產生 AE_WRITEABLE 事件,將數據回復給 client。

文件事件實現總結

我們按照開始給出的 Reactor 模型,從上到下講解了文件事件處理器的實現,下面將會介紹時間時間的實現。

時間事件

Reids 有很多操作需要在給定的時間點進行處理,時間事件就是對這類定時任務的抽象。

先看時間事件的數據結構:

/* Time event structure * * 時間事件結構 */typedef struct aeTimeEvent { // 時間事件的唯一標識符 long long id; /* time event identifier. */ // 事件的到達時間 long when_sec; /* seconds */ long when_ms; /* milliseconds */ // 事件處理函數 aeTimeProc *timeProc; // 事件釋放函數 aeEventFinalizerProc *finalizerProc; // 多路復用庫的私有數據 void *clientData; // 指向下個時間事件結構,形成鏈表 struct aeTimeEvent *next;} aeTimeEvent;

看見 next 我們就知道這個 aeTimeEvent 是一個鏈表結構??磮D:

redis事件驅動,redis,模型,事件模型

注意:這是一個按照id倒序排列的鏈表,并沒有按照事件順序排序。

processTimeEvent

Redis 使用這個函數處理所有的時間事件,我們整理一下執行思路:

  • 記錄最新一次執行這個函數的時間,用于處理系統時間被修改產生的問題。
  • 遍歷鏈表找出所有 when_sec 和 when_ms 小于現在時間的事件。
  • 執行事件對應的處理函數。
  • 檢查事件類型,如果是周期事件則刷新該事件下一次的執行事件。
  • 否則從列表中刪除事件。

綜合調度器(aeProcessEvents)

綜合調度器是 Redis 統一處理所有事件的地方。我們梳理一下這個函數的簡單邏輯:

// 1. 獲取離當前時間最近的時間事件shortest = aeSearchNearestTimer(eventLoop);// 2. 獲取間隔時間timeval = shortest - nowTime;// 如果timeval 小于 0,說明已經有需要執行的時間事件了。if(timeval < 0){ timeval = 0}// 3. 在 timeval 時間內,取出文件事件。numevents = aeApiPoll(eventLoop, timeval);// 4.根據文件事件的類型指定不同的文件處理器if (AE_READABLE) { // 讀事件 rfileProc(eventLoop,fd,fe->clientData,mask);} // 寫事件if (AE_WRITABLE) { wfileProc(eventLoop,fd,fe->clientData,mask);}

以上的偽代碼就是整個 Redis 事件處理器的邏輯。

我們可以再看看誰執行了這個 aeProcessEvents:

void aeMain(aeEventLoop *eventLoop) { eventLoop->stop = 0; while (!eventLoop->stop) {  // 如果有需要在事件處理前執行的函數,那么運行它  if (eventLoop->beforesleep != NULL)   eventLoop->beforesleep(eventLoop);  // 開始處理事件  aeProcessEvents(eventLoop, AE_ALL_EVENTS); }}

然后我們再看看是誰調用了 eaMain:

int main(int argc, char **argv) { //一些配置和準備 ... aeMain(server.el);  //結束后的回收工作 ...}

我們在 Redis 的 main 方法中找個了它。

這個時候我們整理出的思路就是:

  • Redis 的 main() 方法執行了一些配置和準備以后就調用 eaMain() 方法。
  • eaMain() while(true) 的調用 aeProcessEvents()。

所以我們說 Redis 是一個事件驅動的程序,期間我們發現,Redis 沒有 fork 過任何線程。所以也可以說 Redis 是一個基于事件驅動的單線程應用。

總結

在后端的面試中 Redis 總是一個或多或少會問到的問題。

讀完這篇文章你也許就能回答這幾個問題:

為什么 Redis 是一個單線程應用?
為什么 Redis 是一個單線程應用,卻有如此高的性能?
如果你用本文提供的知識點回答這兩個問題,一定會在面試官心中留下一個高大的形象。

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到Redis頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久久久国产免费| 久久精品久久久久| 国产婷婷色综合av蜜臀av| 亚洲国产日韩精品在线| 日本一区二区三区在线播放| 亚洲欧洲国产伦综合| 日韩av高清不卡| 国产成人综合精品在线| 亚洲精品自拍偷拍| 国产精品1234| 久久久999成人| 欧美俄罗斯乱妇| 久久在线免费观看视频| 欧美激情在线狂野欧美精品| 中文综合在线观看| 欧美一级高清免费播放| 57pao成人永久免费视频| 91免费视频网站| 精品亚洲一区二区三区| 亚洲国产古装精品网站| 亚洲精品国产精品自产a区红杏吧| 亚洲香蕉在线观看| 欧美精品久久久久久久| 欧美日韩在线视频一区二区| 日韩欧美国产骚| 国产国产精品人在线视| 精品一区二区三区四区| 亚洲日韩第一页| 亚洲欧美日韩爽爽影院| 自拍偷拍亚洲一区| 91九色精品视频| 全色精品综合影院| 91亚洲精品久久久久久久久久久久| 久久久91精品国产一区不卡| 98午夜经典影视| 欧美成人免费大片| 91免费精品视频| 亚洲精品一区二区三区婷婷月| 色偷偷av一区二区三区乱| 日韩在线中文视频| 国产成人avxxxxx在线看| 伊人久久久久久久久久久久久| 欧美精品免费播放| 亚洲人成电影在线| 亚洲电影中文字幕| 日韩大片免费观看视频播放| 精品中文字幕久久久久久| 中文精品99久久国产香蕉| 国产精品久久久久久久av电影| 亚洲男人天堂2019| 国产精品欧美日韩一区二区| 日韩欧美国产成人| 欧美国产亚洲精品久久久8v| 久久精品99国产精品酒店日本| 国产一区二区免费| 亚洲一区二区久久久久久久| 自拍偷拍亚洲欧美| 亚洲最大在线视频| 亚洲欧美国产精品专区久久| 国产日韩在线视频| 亚洲欧美综合图区| 欧美在线精品免播放器视频| 中文字幕欧美日韩va免费视频| 亚洲人成网站999久久久综合| 91大神福利视频在线| 亚洲国产福利在线| 欧美一区二区影院| 136fldh精品导航福利| 青青精品视频播放| 亚州成人av在线| 在线观看成人黄色| 超碰精品一区二区三区乱码| 久久久久久久久久久网站| 国产精品国模在线| 亚洲伊人久久大香线蕉av| 欧美日韩性视频在线| 成人情趣片在线观看免费| 日韩免费高清在线观看| 国产成人avxxxxx在线看| 欧美日韩国产综合视频在线观看中文| 国产99久久精品一区二区| 亚洲区一区二区| 国产精品自拍网| 国产日韩中文字幕在线| 国产精品视频精品视频| 人九九综合九九宗合| 韩国一区二区电影| 色综合久久88| 日韩午夜在线视频| 色妞欧美日韩在线| 国产成人精品av在线| 亚洲成色www8888| 久久久久久久爱| 成人国产精品久久久| 国产在线久久久| 国产精品视频永久免费播放| 精品高清美女精品国产区| 91亚洲永久免费精品| 国产精品精品国产| 日韩精品欧美国产精品忘忧草| 91精品国产91久久久久久| 欧美日韩爱爱视频| 日韩高清电影免费观看完整| 久久亚洲一区二区三区四区五区高| 亚洲欧美激情四射在线日| 亚洲精品美女在线观看| 久久精品99久久香蕉国产色戒| 欧美与欧洲交xxxx免费观看| 久久天天躁狠狠躁夜夜躁2014| 亚洲免费精彩视频| 欧美激情视频一区二区| 精品国产一区二区三区久久久狼| 欧美日韩免费在线观看| 国产视频一区在线| 91精品国产乱码久久久久久蜜臀| 另类美女黄大片| 国产欧美日韩精品在线观看| 911国产网站尤物在线观看| 国产999精品久久久影片官网| 亚洲人午夜色婷婷| 欧美一区三区三区高中清蜜桃| 欧美日韩激情视频8区| 久久99久久亚洲国产| 91精品久久久久久久久久入口| 国产成人精品久久亚洲高清不卡| 国产狼人综合免费视频| www.日韩不卡电影av| 狠狠躁夜夜躁人人爽天天天天97| 亚洲精品电影在线观看| 午夜精品一区二区三区在线| 欧美激情xxxx| 国产精品久久久久久久一区探花| 国产免费一区二区三区在线能观看| 亚洲欧美三级伦理| 国产日韩专区在线| 中文在线不卡视频| 欧美成人在线免费| 欧美成人小视频| 日韩欧美中文字幕在线观看| 欧美亚洲国产日韩2020| 日韩精品高清在线观看| 亚洲在线视频观看| 国产精品久久久久久久久久久不卡| 色小说视频一区| 色樱桃影院亚洲精品影院| 亚洲一区二区三区乱码aⅴ蜜桃女| 日韩综合视频在线观看| 97在线免费观看视频| 久久久精品美女| 欧美精品videofree1080p| 成人黄在线观看| 国产女精品视频网站免费| 欧美三级欧美成人高清www| 欧美成人黑人xx视频免费观看| 91在线观看免费| 亚洲国产另类 国产精品国产免费| 欧美色xxxx| 在线丨暗呦小u女国产精品| 亚洲免费电影在线观看| 久久久久这里只有精品| xxxx欧美18另类的高清| 欧美性xxxx极品hd满灌| 正在播放欧美一区| 久久久免费观看|