??Redis服務(wù)器是一個事件驅(qū)動程序,服務(wù)器需要處理以下兩類事件:
-
文件事件:Redis服務(wù)器通過套接字(
Socket
)與客戶端 (或其他Redis服務(wù)器)進行連接璧坟,文件事件就是服務(wù)器對套接字操作的抽象抖格。服務(wù)器與客戶端的通信會產(chǎn)生相應(yīng)的文件事件挽绩,二服務(wù)器則通過監(jiān)聽并處理這些事件來完成一系列網(wǎng)絡(luò)通信操作。 - 時間事件:Redis服務(wù)器中的一些操作需要在給定的時間點執(zhí)行稳其,而時間事件就是服務(wù)器低這類定時操作的抽象扩氢。
文件事件
??Redis基于Reactor模式開發(fā)了自己的網(wǎng)絡(luò)事件處理器--文件事件處理器:
- 文件事件處理器使用I/O多路復(fù)用(
multiplexing
)程序來同時監(jiān)聽多個套接字耕驰,并根據(jù)套接字目前執(zhí)行的任務(wù)來為套接字關(guān)聯(lián)不同的事件處理器。 - 當(dāng)被監(jiān)聽的套接字準備好執(zhí)行連接應(yīng)答(
accept
)录豺、讀取(read
)朦肘、寫入(write
)、關(guān)閉(close
)等操作時双饥,與操作相對應(yīng)的文件事件就會產(chǎn)生媒抠,這時文件事件處理器就會調(diào)用套接字之前管理好的事件處理器來處理這些事件。
??雖然文件事件處理器以單線程方式運行咏花,但通過使用I/O多路復(fù)用程序來監(jiān)聽多個套接字趴生,既實現(xiàn)了高性能的網(wǎng)絡(luò)通信模型,又可以很好地與Redis服務(wù)器中其他同樣以單線程方式運行的模塊進行對接昏翰,這保持了Redis內(nèi)部但線程設(shè)計的簡單性苍匆。
??I/O多路復(fù)用技術(shù)見知乎一篇高贊文章解答
文件事件處理器的構(gòu)成
??文件事件處理器由套接字、I/O多路復(fù)用程序棚菊、文件事件分派器和事件處理器構(gòu)成浸踩,如下圖:
??文件事件是對套接字操作的抽象,每當(dāng)一個套接字準備好執(zhí)行連接應(yīng)答统求、寫入民轴、讀取、關(guān)閉等操作時球订,就會產(chǎn)生一個文件事件。因為一個服務(wù)器會連接多個套接字瑰钮,所以多個文件事件可能會并發(fā)地出現(xiàn)冒滩。
??I/O多路復(fù)用程序負責(zé)監(jiān)聽多個套接字,并向文件事件分派器傳送那些產(chǎn)生了事件的套接字浪谴。
??盡管多個文件事件可能會并發(fā)地出現(xiàn)开睡,但I/O多路復(fù)用程序總是會將所有產(chǎn)生事件的套接字都放到一個隊列里面,然后通過這個隊列苟耻,以有序(sequentially
)篇恒、同步(synchronously
)、每次一個套接字的方式向文件事件分派器傳送套接字凶杖。當(dāng)一個套接字產(chǎn)生的事件被處理完畢后(該套接字為事件所關(guān)聯(lián)的事件處理器執(zhí)行完畢)胁艰,I/O多路復(fù)用程序才會繼續(xù)向文件事件分派傳送下一個套接字,如下圖:
??文件事件分派器接收到I/O多路復(fù)用程序傳來的套接字,并根據(jù)套接字產(chǎn)生的事件類型調(diào)用相應(yīng)的事件處理器腾么。
??服務(wù)器會為執(zhí)行不同任務(wù)的套接字關(guān)聯(lián)不同的事件處理器奈梳,這些處理器是一個個函數(shù),它們定義了某個事件發(fā)生時解虱,服務(wù)器應(yīng)該執(zhí)行的動作
I/O多路復(fù)用程序的實現(xiàn)
??Redis的I/O多路復(fù)用程序的所有功能都是通過包裝常見的select攘须、epoll、evport殴泰、kqueue
這些I/O多路復(fù)用函數(shù)庫實現(xiàn)的于宙。因為Redis為每個函數(shù)庫都實現(xiàn)了相同的API,所以I/O多路復(fù)用程序的底層實現(xiàn)是可以互換的(Redis定義相應(yīng)的規(guī)則所以在編譯時自動選擇性能最高的庫來作為其底層實現(xiàn))悍汛,如下圖:
事件的類型
??I/O多路復(fù)用程序可以監(jiān)聽多個套接字的ae.h/AE_READABLE
事件和ae.h/AE_WRITABLE
事件捞魁,這連累事件和套接字操作之間的關(guān)系如下:
- 當(dāng)套接字變得可讀時(客戶端對套接字執(zhí)行
write
操作,或者執(zhí)行close
操作)员凝,或者有新的可應(yīng)答(acceptable
)套接字出現(xiàn)時(客戶端對服務(wù)器的監(jiān)聽套接字執(zhí)行connect
操作)署驻,套接字產(chǎn)生AE_READABLE
事件 - 當(dāng)套接字變得可寫時(客戶端對套接字執(zhí)行
read
操作),套接字產(chǎn)生AE_WRITABLE
事件
??I/O多路復(fù)用程序運行服務(wù)器同時監(jiān)聽套接字的AE_READABLE
事件和AE_WRITABLE
事件,如果一個套接字同時產(chǎn)生了這兩種事件,那么文件事件分派器優(yōu)先處理AE_READABLE
事件健霹,該事件處理完之后再處理AE_WRITABLE
事件旺上。即套接字可讀又可寫的話,則服務(wù)器先讀套接字糖埋,后寫套接字宣吱。
文件事件的處理器
??Redis為文件事件編寫了多個處理器,分別用于實現(xiàn)不同的網(wǎng)絡(luò)通信需求瞳别,下面簡單介紹:
- 連接應(yīng)答處理器:為了對連接服務(wù)器的各個客戶端進行應(yīng)答征候,服務(wù)器為監(jiān)聽套接字關(guān)聯(lián)該處理器
- 關(guān)聯(lián)命令請求處理器:為了介紹客戶端傳來的命令請求,服務(wù)器為客戶端套接字關(guān)聯(lián)該處理器
- 關(guān)聯(lián)命令回復(fù)處理器:為了向客戶端返回命令的執(zhí)行結(jié)果祟敛,服務(wù)器為客戶端套接字關(guān)聯(lián)該處理器
- 復(fù)制處理器:當(dāng)主服務(wù)器和從服務(wù)器進行復(fù)制操作時疤坝,主從服務(wù)器都需要關(guān)聯(lián)該處理器
時間事件
??Redis的時間事件分為下面2類:
- 定時事件:讓一段程序在指定時間之后執(zhí)行一次。比如說讓程序X在當(dāng)前事件的30ms之后執(zhí)行一次
-
周期性事件:讓一段程序每隔指定時間執(zhí)行一次馆铁。比如說讓程序Y每個30ms就執(zhí)行一次
??一個時間事件主要由以下3個屬性組成: -
id
:服務(wù)器為時間事件創(chuàng)建的全局唯一ID跑揉。ID按從小到大的順序遞增,新事件的ID號比舊事件大 -
when
:毫秒精度的UNIX時間戳埠巨,記錄了時間事件的到達時間 -
timeProc
:時間事件處理器函數(shù)历谍。當(dāng)時間事件到達時,服務(wù)器就會調(diào)用相應(yīng)的處理器來處理事件辣垒。
??一個時間事件是定時事件還是周期性事件取決于時間事件處理器的返回值望侈。
時間事件實現(xiàn)
??服務(wù)器將所有時間事件都放在一個無序鏈表中,當(dāng)時間事件處理器被執(zhí)行時勋桶, 它遍歷所有鏈表中的時間事件脱衙, 檢查它們的到達事件(when 屬性)侥猬, 并執(zhí)行其中的已到達事件
注:
正常模式下的Redis服務(wù)器只使用serverCron
一個時間事件。
時間事件應(yīng)用實例:serverCron函數(shù)
??持續(xù)運行的Redis服務(wù)器需要定期對自身資源和狀態(tài)進行檢查和調(diào)整岂丘,從而確保服務(wù)器可以長期陵究、穩(wěn)定地運行,這些定期操作由redis.c/serverCrom
函數(shù)負責(zé)執(zhí)行奥帘,它的主要工作包括:
- 更新服務(wù)器的各類統(tǒng)計信息铜邮,比如時間、內(nèi)存占用寨蹋、數(shù)據(jù)庫占用情況等
- 清理數(shù)據(jù)庫中的過期鍵值對
- 關(guān)閉和清理連接失效的客戶端
- 嘗試進行AOF或RDB持久化操作
- 如果服務(wù)器是主服務(wù)器松蒜,那么對從服務(wù)器進行定期同步
-
如果處于集群模式,對集群進行定期同步和連接測試
??Redis服務(wù)器以周期性事件的方式來運行serverCron
函數(shù)已旧,在服務(wù)器運行期間秸苗,每隔一段時間,該函數(shù)執(zhí)行一次运褪,直到服務(wù)器關(guān)閉為止惊楼。服務(wù)器默認規(guī)定serverCron
函數(shù)每秒運行10次,平均每間隔100ms運行一次秸讹,我們也可以修改redis.conf
配置文件的hz
選項來調(diào)整其每秒執(zhí)行次數(shù)檀咙。
事件的調(diào)度與執(zhí)行
??因為服務(wù)器中同時存在文件事件和時間事件,所以服務(wù)器必須對這兩種事件進行調(diào)度璃诀,決定何時應(yīng)該處理文件事件弧可,何時處理時間事件,以及花多少時間來處理它們等等劣欢。
??文件事件和時間事件之間是合作關(guān)系棕诵,服務(wù)器會輪流處理這兩種事件,并且處理過程中也不會進行互相搶占凿将,但因此實際處理時間事件的事件比預(yù)定到達的時間會晚一些校套。
參考資料
《redis設(shè)計與實現(xiàn)》(第二版)