12 事件

Redis服務(wù)器是一個事件驅(qū)動程序盐碱,服務(wù)器需要處理以下兩類事件:

  • 文件事件(file event):Redis服務(wù)器通過套接字與客戶端進(jìn)行連接,而文件事件就是服務(wù)器對套接字操作的抽象番甩。服務(wù)器和客戶端的通信會產(chǎn)生相應(yīng)的文件事件,而服務(wù)器則通過監(jiān)聽并處理這些事件來完成一系列的網(wǎng)路通信操作。
  • 時間事件(time event):Redis服務(wù)器中的一些操作(serverCron函數(shù))需要在給定的時間點(diǎn)執(zhí)行,而時間事件就是服務(wù)器對這類定時操作的抽象著蛙。

12.1 文件事件

Redis基于Reactor模式開發(fā)了自己的網(wǎng)絡(luò)事件處理器:這個處理器被稱為文件事件處理器(file event handler):

  • 文件事件處理器使用I/O多路復(fù)用程序來同時監(jiān)聽多個套接字,并根據(jù)套接字目前執(zhí)行的任務(wù)來為套接字關(guān)聯(lián)不同的時間處理器耳贬。
  • 當(dāng)被監(jiān)聽的套接字準(zhǔn)備好執(zhí)行連接應(yīng)答(accept)踏堡、讀取(read)、寫入(write)咒劲、關(guān)閉(close)等操作時顷蟆,與操作相對應(yīng)的文件事件就會產(chǎn)生胖秒,這時文件事件處理器就會調(diào)用套接字之前關(guān)聯(lián)好的事件處理器來處理這些事件。

雖然文件事件處理器以單線程方式運(yùn)行慕的,但通過使用I/O多路復(fù)用程序來監(jiān)聽多個套接字阎肝,文件事件處理器既實現(xiàn)了高性能的網(wǎng)絡(luò)通信模型,又可以很好地與Redis服務(wù)器中其他同樣以單線程方式運(yùn)行的模塊進(jìn)行對接肮街,這保持了Redis內(nèi)部單線程設(shè)計的簡單性风题。

12.1.1 文件事件處理器的構(gòu)成

文件事件處理器的四個組成部分

文件事件處理器有四個部分組成,分別是套接字嫉父、I/O多路復(fù)用程序沛硅、文件事件分派器,以及事件處理器绕辖。
文件事件是對套接字操作的抽象摇肌,每當(dāng)一個套接字準(zhǔn)備好執(zhí)行連接應(yīng)答(accept)、寫入仪际、讀取围小、關(guān)閉等操作時,就會產(chǎn)生一個文件事件树碱。因為一個服務(wù)器通常會連接多個套接字肯适,所以多個文件事件可能會并發(fā)地出現(xiàn)。
I/O多路復(fù)用程序負(fù)責(zé)監(jiān)聽多個套接字成榜,并向文件事件分派器傳送那些產(chǎn)生了事件的套接字框舔。
盡管多個文件事件可能會并發(fā)地出現(xiàn),但I/O多路復(fù)用程序總是會將所有產(chǎn)生事件的套接字都放到一個隊列里面赎婚,然后通過這個隊列刘绣,以有序、同步挣输、每次一個套接字的方式向文件事件分派器傳送套接字纬凤。當(dāng)上一個套接字產(chǎn)生的事件被處理完畢之后,I/O多路復(fù)用程序才會繼續(xù)向文件事件分派器傳送下一個套接字歧焦。

I/O多路復(fù)用程序通過隊列向文件事件分派器傳送套接字

文件事件分派器接受I/O多路復(fù)用程序傳來的套接字移斩,并根據(jù)套接字產(chǎn)生的事件類型,調(diào)用相應(yīng)的事件處理器绢馍。
服務(wù)器會為執(zhí)行不同任務(wù)的套接字關(guān)聯(lián)不同的事件處理器向瓷,這些處理器是一個個函數(shù),它們定義了某個事件發(fā)生時舰涌,服務(wù)器應(yīng)該執(zhí)行的動作猖任。

12.1.2 I/O多路復(fù)用程序的實現(xiàn)

RedisI/O多路復(fù)用程序的所有功能都是通過包裝創(chuàng)建的selectepoll瓷耙、evportkqueue這些I/O多路復(fù)用函數(shù)庫來實現(xiàn)的朱躺,每個I/O多路復(fù)用函數(shù)庫在Redis源碼中都對應(yīng)一個單獨(dú)的源文件刁赖。
因為Redis為每個I/O多路復(fù)用函數(shù)庫都實現(xiàn)了相同的API,所以I/O多路復(fù)用程序的底層實現(xiàn)時可以互換的长搀。

12.1.3 事件類型

I/O多路復(fù)用程序可以監(jiān)聽多個套接字的ae.h/AE_READABLE事件和ae.h/AE_WRITABLE事件宇弛,這兩類事件和套接字操作之間的對應(yīng)關(guān)系如下:

  • 當(dāng)套接字變得可讀時(客戶端對套接字執(zhí)行write操作,或者執(zhí)行close操作)源请,或者有新的可應(yīng)答(acceptable)套接字出現(xiàn)時(客戶端對服務(wù)器的監(jiān)聽套接字執(zhí)行connection操作)枪芒,套接字產(chǎn)生AE_READABLE事件。
  • 當(dāng)套接字變得可寫時(客戶端對套接字執(zhí)行read操作)谁尸,套接字產(chǎn)生AE_WRITABLE事件舅踪。

I/O多路復(fù)用程序允許服務(wù)器同時監(jiān)聽套接字的AE_READABLE事件和AE_WRITEABLE事件,如果一個套接字同時產(chǎn)生了這兩種事件良蛮,那么文件事件分派器會有限處理AE_READABLE事件抽碌,等到AE_READABLE事件處理完之后,才處理AE_WRITEABLE事件决瞳。

12.1.4 API

略(可以參考Netty和Linux)

12.1.5 文件事件處理器

Redis為文件事件編寫了多個處理器货徙,這些事件處理器分別用于實現(xiàn)不同的網(wǎng)絡(luò)通信需要。

  • 為了對連接服務(wù)器的各個客戶端進(jìn)行應(yīng)答瞒斩,服務(wù)器要為監(jiān)聽套接字關(guān)聯(lián)連接應(yīng)答服務(wù)器破婆。
  • 為了接受客戶端傳來的命令請求涮总,服務(wù)器腰圍客戶端套接字關(guān)聯(lián)命令請求處理器
  • 為了向客戶端返回命令的執(zhí)行結(jié)果胸囱,服務(wù)器要為客戶端套接字關(guān)聯(lián)命令回復(fù)處理器。
  • 當(dāng)主服務(wù)器和從服務(wù)器進(jìn)行復(fù)制操作時瀑梗,主從服務(wù)器都需要關(guān)聯(lián)特別為復(fù)制功能編寫的復(fù)制處理器烹笔。

服務(wù)器最常用的是與客戶端進(jìn)行通信的連接應(yīng)答處理器、命令請求處理器和命令回復(fù)處理器抛丽。

1. 連接應(yīng)答處理器

networking.c/acceptTcpHandler函數(shù)是Redis的連接應(yīng)答處理器谤职,這個處理器用于對連接服務(wù)器監(jiān)聽套接字的客戶端進(jìn)行應(yīng)答错森,具體實現(xiàn)為sys/socket.h/accept函數(shù)的包裝削咆。
當(dāng)Redis服務(wù)器進(jìn)行初始化的時候室梅,程序會將這個連接應(yīng)答處理器和服務(wù)器監(jiān)聽套接字的AE_READABLE事件關(guān)聯(lián)起來根灯,當(dāng)有客戶端用sys/socket.h/connect函數(shù)連接服務(wù)器監(jiān)聽套接字的時候踏幻,套接字就會產(chǎn)生AE_READABLE事件客扎,引發(fā)連接應(yīng)答處理執(zhí)行鞋喇,并執(zhí)行相應(yīng)的套接字應(yīng)答操作芒填。

服務(wù)器對客戶端的連接請求進(jìn)行應(yīng)答

2. 命令請求處理器

networking.c/readQueryFromClient函數(shù)是Redis的命令請求處理器垒探,這個處理器負(fù)責(zé)從套接字中讀入客戶端發(fā)送的命令請求內(nèi)容妓蛮,具體實現(xiàn)為unistd.h/read函數(shù)的包裝。
當(dāng)一個客戶端通過連接應(yīng)答處理器成功連接到服務(wù)器之后圾叼,服務(wù)器會將客戶端套接字的AE_READABLE事件和命令請求處理關(guān)聯(lián)起來蛤克,當(dāng)客戶端向服務(wù)器發(fā)送命令請求的時候捺癞,套接字就會產(chǎn)生AE_READABLE事件,引發(fā)命令請求處理器執(zhí)行构挤,并執(zhí)行相應(yīng)的套接字讀入操作髓介。
在客戶端連接服務(wù)器的整個過程中,服務(wù)器都會一直為客戶端套接字的AE_READABLE事件關(guān)聯(lián)命令請求處理器筋现。

服務(wù)端接受客戶端發(fā)來的命令請求

3. 命令回復(fù)處理器

networking.c/sendReplyToClient函數(shù)是Redis的命令回復(fù)處理器版保,這個處理器負(fù)責(zé)將服務(wù)器執(zhí)行命令后得到的命令回復(fù)通過套接字返回給客戶端。具體實現(xiàn)為unistd.h/write函數(shù)的包裝夫否。
當(dāng)服務(wù)器有命令回復(fù)需要傳送給客戶端的時候彻犁,服務(wù)器會將客戶端套接字的AE_WRITEABLE事件和命令回復(fù)處理器關(guān)聯(lián)起來,當(dāng)客戶端準(zhǔn)備好接受服務(wù)器傳回的命令回復(fù)時凰慈,就會產(chǎn)生AE_WRITEABLE事件汞幢,引發(fā)命令回復(fù)處理器執(zhí)行,并執(zhí)行相應(yīng)的套接字寫入操作微谓。
當(dāng)命令回復(fù)發(fā)送完畢之后森篷,服務(wù)器就會解除命令回復(fù)處理器與客戶端套接字的AE_WRITEABLE事件之間的關(guān)聯(lián)。

服務(wù)端向客戶端發(fā)送命令回復(fù)

4. 一次完整的客戶端與服務(wù)器連接事件示例

假設(shè)一個Redis服務(wù)器正在運(yùn)作豺型,那么這個服務(wù)器的監(jiān)聽套接字的AE_READABLE事件應(yīng)該正處于監(jiān)聽狀態(tài)之下仲智,而該事件所對應(yīng)的處理器為連接應(yīng)答處理器。
如果這時有一個Redis客戶端向服務(wù)器發(fā)起連接姻氨,那么監(jiān)聽調(diào)節(jié)自將產(chǎn)生AE_READABLE事件钓辆,觸發(fā)連接應(yīng)答處理器執(zhí)行。處理器會對客戶端的連接請求進(jìn)行應(yīng)答肴焊,然后創(chuàng)建客戶端套接字前联,以及客戶端狀態(tài),并將客戶端套接字的AE_READABLE事件與命令請求處理器進(jìn)行關(guān)聯(lián)娶眷,是的客戶端可以向主服務(wù)器發(fā)送命令請求似嗤。
之后,客戶端向主服務(wù)器發(fā)送一個命令請求届宠,那么客戶端套接字將產(chǎn)生AE_READABLE事件烁落,引發(fā)命令請求處理器執(zhí)行,處理器讀取客戶端的命令內(nèi)容豌注,然后傳給相關(guān)程序去執(zhí)行伤塌。
執(zhí)行命令將產(chǎn)生相應(yīng)的命令回復(fù),為了將這些命令回復(fù)傳送回客戶端幌羞,服務(wù)器會將客戶端套接字的AE_WRTIEABLE事件與命令處理器進(jìn)行關(guān)聯(lián)寸谜,當(dāng)客戶端嘗試讀取命令回復(fù)的時候,客戶端套接字將產(chǎn)生AE_WRITEABLE事件,觸發(fā)命令回復(fù)處理器執(zhí)行熊痴,當(dāng)命令回復(fù)處理器將命令回復(fù)全部寫入到套接字之后他爸,服務(wù)器就會接觸客戶端套接字的AE_WRITABLE事件與命令回復(fù)處理器之間的關(guān)聯(lián)。

客戶端與服務(wù)器端的通信過程

12.2 時間事件

Redis的時間事件分為以下兩類:

  • 定時事件:讓一段程序在指定的時間之后執(zhí)行一次果善。
  • 周期性事件:讓一段程序每隔指定事件就執(zhí)行一次诊笤。

一個時間事件主要由以下三個屬性組成:
-id:服務(wù)器為時間事件創(chuàng)建的全局唯一ID(標(biāo)識號)。ID號從小到大的順序遞增巾陕,新事件的ID號比舊事件的ID號要大讨跟。

  • when:毫秒級別的UNIX時間戳,記錄了時間事件的到達(dá)事件鄙煤。
  • timeProc:時間事件處理器晾匠,一個函數(shù)。

一個時間事件是定時是事件還是周期性事件取決于時間事件處理器的返回值:

  • 如果事件處理器返回ae.h/AE_NOMORE梯刚,那么這個事件為定時事件:該事件在達(dá)到一次之后就會被刪除凉馆,之后不在到達(dá)。
  • 如果事件處理器返回一個非AE_NOMORE的整數(shù)值亡资,那么這個事件為周期性事件:當(dāng)一個時間事件到達(dá)之后澜共,服務(wù)器會根據(jù)事件處理器返回的值,對時間事件的when屬性進(jìn)行跟新锥腻,讓這個事件在一段時間之后再次到達(dá)嗦董,一直更新并運(yùn)行下去。
    目前版本的Redis只有用周期性事件瘦黑,而沒有使用定時事件京革。

12.2.1 實現(xiàn)

服務(wù)器將所有時間事件都放在一個無序鏈表中,每當(dāng)時間事件執(zhí)行器運(yùn)行時供璧,它就遍歷整個鏈表存崖,查找所有已到達(dá)的時間事件,并調(diào)用相應(yīng)的時間處理睡毒。
新的時間事件總是插入到鏈表的表頭,所以表頭的ID最大冗栗,表尾的ID最小演顾。
注意:保存時間事件的鏈表為無序鏈表,指的不是鏈表不按ID排序隅居,而是說钠至,該鏈表不按when屬性的大小排序。正因為鏈表沒有按when屬性進(jìn)行排序胎源,所以當(dāng)時間事件執(zhí)行器運(yùn)行的時候棉钧,它必須遍歷鏈表中的所有時間事件,這樣才能確保服務(wù)器中所有已到達(dá)的時間事件都會被處理涕蚤。

12.2.2 API

12.2.3 時間事件應(yīng)用實例:serverCron函數(shù)

持續(xù)運(yùn)行的Redis服務(wù)器需要定期對自身的資源和狀態(tài)進(jìn)行檢查和調(diào)整宪卿,從而確保服務(wù)器可以長期的诵、穩(wěn)定的運(yùn)行,這些定期操作由redis.c/serverCron函數(shù)負(fù)責(zé)執(zhí)行佑钾,它的主要工作包括:

  • 更新服務(wù)器的各類統(tǒng)計信息西疤,比如時間、內(nèi)次占用休溶、數(shù)據(jù)庫占用情況等代赁。
  • 清理數(shù)據(jù)庫中的過期鍵值對。
  • 關(guān)閉和清理連接失效的客戶端兽掰。
  • 嘗試進(jìn)行AOFRDB持久化操作
  • 如果服務(wù)器是主服務(wù)器芭碍,那么對從服務(wù)器進(jìn)行定期同步
  • 如果處于集群模式,對集群進(jìn)行定期同步和連接測試孽尽。

Redis服務(wù)器以周期性事件的方式來運(yùn)行serverCron函數(shù)豁跑,在服務(wù)器運(yùn)行期間,每個一段時間泻云,serverCron就會執(zhí)行一次艇拍,知道服務(wù)器關(guān)閉為止。
默認(rèn)規(guī)定serverCron每秒運(yùn)行10次宠纯,平均每間隔100毫秒運(yùn)行一次卸夕。

12.3 事件的調(diào)度與執(zhí)行

因為服務(wù)器中同時存在文件事件和時間事件兩種事件類型,所以服務(wù)器必須對這兩種事件進(jìn)行調(diào)度婆瓜,決定何時應(yīng)該處理文件事件快集,何時又應(yīng)該處理事件事件,以及花多少事件來處理他們廉白。


事件處理角度下的服務(wù)器運(yùn)行流程

以下是事件的調(diào)度和執(zhí)行規(guī)則

  1. aeApiPoll函數(shù)的最大阻塞時間由到達(dá)時間最近當(dāng)前時間的時間事件決定个初,這個方法既可以避免服務(wù)器對時間事件進(jìn)行頻繁的輪詢,也可以確保aeApiPoll函數(shù)不會阻塞過長的時間猴蹂。
  2. 因為文件事件是隨機(jī)出現(xiàn)的院溺,如果等待并處理完一次文件事件之后,仍未有任何時間事件到達(dá)磅轻,那么服務(wù)器將再次等待并處理文件事件珍逸。隨著文件事件的不斷執(zhí)行,事件會逐漸向時間事件所設(shè)置的到達(dá)時間逼近聋溜,并最終來到到達(dá)時間谆膳,這時服務(wù)器就可以開始處理到達(dá)的時間事件了。
  3. 對文件事件和時間事件的處理都是同步撮躁、有序漱病、原子的執(zhí)行的,服務(wù)器不會中途中斷事件處理,也不會對事件進(jìn)行搶占杨帽,因此漓穿,不管是文件事件的處理器,還是事件事件的處理器睦尽,他們都會盡可能地減少程序的阻塞事件器净,并在有需要的時候主動讓出執(zhí)行權(quán),從而降低造成事件饑餓的可能性当凡。另外山害,事件事件也會將非常耗時的持久化操作放到子線程或者子進(jìn)程中執(zhí)行。
  4. 因為時間事件在文件事件之后執(zhí)行沿量,并且事件之間不會出現(xiàn)搶占浪慌,所以時間事件的實際處理事件,通常會比時間事件設(shè)定的到達(dá)時間稍微晚一些朴则。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末权纤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子乌妒,更是在濱河造成了極大的恐慌汹想,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撤蚊,死亡現(xiàn)場離奇詭異古掏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)侦啸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門槽唾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人光涂,你說我怎么就攤上這事庞萍。” “怎么了忘闻?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵钝计,是天一觀的道長。 經(jīng)常有香客問我服赎,道長葵蒂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任重虑,我火速辦了婚禮,結(jié)果婚禮上秦士,老公的妹妹穿的比我還像新娘缺厉。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布提针。 她就那樣靜靜地躺著命爬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辐脖。 梳的紋絲不亂的頭發(fā)上饲宛,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音嗜价,去河邊找鬼艇抠。 笑死,一個胖子當(dāng)著我的面吹牛久锥,可吹牛的內(nèi)容都是我干的家淤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼瑟由,長吁一口氣:“原來是場噩夢啊……” “哼絮重!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起歹苦,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤青伤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后殴瘦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狠角,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年痴施,在試婚紗的時候發(fā)現(xiàn)自己被綠了擎厢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡辣吃,死狀恐怖动遭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情神得,我是刑警寧澤厘惦,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站哩簿,受9級特大地震影響宵蕉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜节榜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一羡玛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宗苍,春花似錦稼稿、人聲如沸薄榛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敞恋。三九已至,卻和暖如春谋右,著一層夾襖步出監(jiān)牢的瞬間硬猫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工改执, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啸蜜,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓天梧,卻偏偏與公主長得像盔性,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子呢岗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Redis服務(wù)器是一個事件驅(qū)動程序冕香,服務(wù)器需要處理以下兩類事件: 1.文件事件:Redis服務(wù)器通過套接字與客戶端...
    Felicia1993閱讀 271評論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)后豫,斷路器悉尾,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 1.Redis特性 1)速度快:數(shù)據(jù)存放在內(nèi)存上、基于C語言實現(xiàn)挫酿、單線程架構(gòu)預(yù)防多線程競爭問題构眯;2)基于鍵值對的數(shù)...
    Sponge1128閱讀 616評論 0 1
  • 今天給寶馬換壓縮機(jī) 換完后剛想往里加氟才想起來沒有報壓看看里面漏沒漏的地方。后來就去拿機(jī)器進(jìn)行保壓果然有漏的地方早龟。...
    京心達(dá)侯天祥閱讀 177評論 0 0
  • Who Is Jane Goodall? Did you know that a girl whose dream...
    影徒隨我閱讀 957評論 0 1