事件
redis服務(wù)器是一個事件驅(qū)動程序,主要處理兩類事件:文件事件和時間事件。
文件事件
文件事件處理器使用I/O多路復(fù)用的程序來同時監(jiān)聽多個套接字,雖然redis的文件事件處理器以單線程方式運(yùn)行,但通過io多路復(fù)用監(jiān)聽多個套接字,這樣實(shí)現(xiàn)了高性能的網(wǎng)絡(luò)通訊模型,又可以很好地讓redis以單線程的方式運(yùn)行,保持了單線程設(shè)計(jì)的簡單性。(這是redis單線程還能那么快的原因之一)
文件事件的構(gòu)成
由四個組成部分:套接字,io多路復(fù)用程序,文件事件分派器以及事件處理器。

當(dāng)套接字變得可讀(客戶端對套接字執(zhí)行write操作或者執(zhí)行close操作)的時候,或者有新的可應(yīng)答套接字出現(xiàn)時,套接字產(chǎn)生AE_READABLE事件。
當(dāng)套接字變得可寫時(客戶端對套接字執(zhí)行read操作),套接字產(chǎn)生AE_WRITABLE事件。
- 一次完整的連接通訊流程是怎么樣子的?
假設(shè)一個redis服務(wù)器正在運(yùn)作,這個時候服務(wù)器的監(jiān)聽套接字的AE_READABLE事件處于監(jiān)聽情況下。
這時有個redis客戶端向服務(wù)器發(fā)起連接,那么監(jiān)聽套接字將產(chǎn)生AE_READABLE事件,觸發(fā)連接應(yīng)答處理器執(zhí)行。
連接處理器應(yīng)答之后會創(chuàng)建客戶端套接字,客戶端狀態(tài),并將客戶端套接字的AE_READABLE事件與命令請求處理器進(jìn)行關(guān)聯(lián)。
然后假設(shè)客戶端向主服務(wù)器發(fā)送一個命令請求,那么客戶端套接字將產(chǎn)生AE_READABLE事件,引發(fā)命令請求處理器執(zhí)行,處理器讀取相關(guān)的命令內(nèi)容,傳給相關(guān)的程序執(zhí)行。
之后命令會產(chǎn)生相關(guān)的恢復(fù),為了將這個回復(fù)給客戶端,服務(wù)器會將客戶端套接字的AE_WRITABLE事件與命令回復(fù)處理器關(guān)聯(lián)。當(dāng)客戶端嘗試讀取命令回復(fù)的時候,客戶端套接字會產(chǎn)生AE_WRITABLE事件,觸發(fā)命令回復(fù)處理器執(zhí)行,當(dāng)命令回復(fù)處理器將命令回復(fù)全部寫入到套接字后,服務(wù)器就會解除客戶端 套接字的事件和關(guān)聯(lián)。

時間事件
redis的時間事件是用周期性事件(讓一個程序每隔指定時間就執(zhí)行一次)
主要有三個屬性組成,id(唯一標(biāo)識號),when(時間事件的到達(dá)時間),timeProc(時間事件處理函數(shù))
- 那具體是怎么實(shí)現(xiàn)的?
redis服務(wù)器將所有的時間事件都放在一個無序列表中,事件執(zhí)行器會去遍歷這些節(jié)點(diǎn),如果發(fā)現(xiàn)時間到達(dá),就會執(zhí)行對應(yīng)的時間事件。
- 這樣實(shí)現(xiàn)不會很耗費(fèi)資源嗎?
不會 ,因?yàn)閞edis的時間事件很少,正常模式下只有serverCron一個事件。
- serverCron函數(shù)是干嘛的?
主要工作有:
- 更新服務(wù)器的各類統(tǒng)計(jì)消息,比如時間,內(nèi)存占用,數(shù)據(jù)庫占用等。
- 清理數(shù)據(jù)庫中的過期鍵值對。
- 關(guān)閉和清理鏈接失效的客戶端。
- 嘗試進(jìn)行AOF或RDB持久化操作。
- 如果服務(wù)器是主服務(wù)器,那么對服務(wù)器進(jìn)行定期同步。
- 如果處于集群模式,對集群進(jìn)行定期同步和連接測試。
調(diào)度
- 因?yàn)榉?wù)器存在文件事件和時間事件,他們是怎么調(diào)度執(zhí)行的?
很簡單,ae.c/aeProcessEvents負(fù)責(zé),如果有時間事件要處理,就不阻塞去處理時間事件,如果沒有時間時間,會阻塞一下,來等待文件事件,然后執(zhí)行文件事件,執(zhí)行時間事件,然后不停死循環(huán)執(zhí)行。
客戶端
redis保存了客戶端當(dāng)前的狀態(tài)信息,以及執(zhí)行相關(guān)功能時需要用到的數(shù)據(jù)結(jié)構(gòu),其中包括:
- 客戶端的套接字描述符(偽客戶端是-1,在aof恢復(fù)用到,否則大于1,每個客戶端都是唯一的)。
- 客戶端的名字。(通過setname設(shè)置)
- 客戶端的標(biāo)識符。(標(biāo)識客戶端的角色(從服務(wù)器,微客戶端)和客戶端的狀態(tài)(執(zhí)行monitor命令等))
- 客戶端正在使用的數(shù)據(jù)庫的指針,已經(jīng)該數(shù)據(jù)庫的號碼。
- 客戶端當(dāng)前要執(zhí)行的命令,命令的參數(shù),命令參數(shù)的個數(shù),以及指向命令實(shí)現(xiàn)函數(shù)的指針。(從緩存區(qū)分析的到的命令內(nèi)容)
- 客戶端的輸入緩沖區(qū)和輸出緩沖區(qū)。(緩存了客戶端的命令以及服務(wù)端的輸出)
- 客戶端的復(fù)制狀態(tài)信息,以及進(jìn)行復(fù)制所需的數(shù)據(jù)結(jié)構(gòu)。
- 客戶端執(zhí)行BRPOP,BLPOP等列表阻塞命令時使用的數(shù)據(jù)結(jié)構(gòu)。
- 客戶端的事務(wù)狀態(tài),以及執(zhí)行WATCH命令時用到的數(shù)據(jù)結(jié)構(gòu)。
- 客戶端執(zhí)行發(fā)布與訂閱功能用到的數(shù)據(jù)結(jié)構(gòu)。
- 客戶端的身份驗(yàn)證標(biāo)志。
- 客戶端的創(chuàng)建時間,最后一次通信時間。
是通過鏈表把多個客戶端狀態(tài)記錄到服務(wù)器中。
- 客戶端的關(guān)閉時機(jī)是什么時候?
- 客戶端退出。
- 客戶端發(fā)送了不符合協(xié)議內(nèi)容的數(shù)據(jù)。
- 服務(wù)端調(diào)用CLIENT KILL
- 服務(wù)端設(shè)置了timeout配置,那么客戶端空轉(zhuǎn)太長就會被關(guān)閉。
- 客戶端的命令大小超過1G。
- 輸出緩沖區(qū)大于限制大小會被關(guān)閉。
服務(wù)器
命令執(zhí)行器是如何工作的?
命令執(zhí)行器首先根據(jù)argv[0]的值,在命令表中找到對應(yīng)的redisCommand對象。
這個對象記錄了命令相關(guān)的細(xì)節(jié),比如允許參數(shù)多少,實(shí)現(xiàn)函數(shù)指針,對該命令的標(biāo)識符,以及一些統(tǒng)計(jì)信息。

服務(wù)器先利用這些對命令進(jìn)行檢查,如果檢查失敗了,則返回一個錯誤,如果打開了監(jiān)視器,就會把要執(zhí)行的命令和參數(shù)等消息發(fā)給監(jiān)視器。
檢查完畢之后,就調(diào)用執(zhí)行命令的相關(guān)函數(shù),得到回復(fù)之后會保存在客戶端狀態(tài)的輸出緩沖區(qū)里面。
執(zhí)行結(jié)束之后還有一些特殊任務(wù),有統(tǒng)計(jì)慢查詢,修改鏈接的統(tǒng)計(jì)信息,aof寫入到AOF緩沖區(qū)里面,復(fù)制命令到其它從服務(wù)器。
最后套接字變?yōu)榭蓪憼顟B(tài)的時候,把輸出緩沖區(qū)里面的數(shù)據(jù)返回給客戶端。
- serverCron函數(shù)詳細(xì)作用?
- 更新緩存服務(wù)器的lruclock時鐘,算每個key的lru值并不是實(shí)時的,而是這個值減去每個key的lru的值。
- info中的一些統(tǒng)計(jì)信息。
- 處理sigterm信號,在退出redis之前做相關(guān)的操作,比如RDB持久化。
- 關(guān)閉無用客戶端連接。
- 抽查部分key,并刪除過期的部分key。
- 將延遲的FGREWRITEAOF執(zhí)行。
- 將AOF緩沖區(qū)的內(nèi)容寫入AOF文件。