Redis服務器是一個事件驅動程序,服務器須要處理如下兩類事件服務器
1:文件事件(file event):Redis服務器經過套接字與客戶端進行鏈接,而文件事件就是服務器對套接字操做的抽象。服務器與客戶端的通訊會產生相應的文件事件,而服務器則經過監聽並處理這些事件來完成一系列網絡通訊操做網絡
2:時間事件(time event):Redis服務器中的一些操做(好比serverCron函數)須要在給定的時間點執行,而時間事件就是服務器對這類定時操做的抽象併發
文件事件函數
Redis基於Reactor模式開發了本身的網絡事件處理器:這個處理器被稱爲文件事件處理器(file event handler)性能
1:文件事件處理器使用I/O多路複用程序來同時監聽多個套接字,並根據套接字目前執行的任務來爲套接字關聯不一樣的事件處理器線程
2:當被監聽的套接字準備好執行鏈接應答(accept)、讀取(read)、寫入(write)、關閉(close)等操做時,與操做相對應的文件事件就會發生,這時文件事件處理器就會調用套接字以前關聯好的事件處理器來處理這些事件設計
雖然文件事件處理器以單線程方式運行,但經過使用I/O多路複用程序來監聽多個套接字,文件事件處理器既實現了高性能的網絡通訊模型,又能夠很好地與Redis服務器中其餘一樣以單線程方式運行的模塊進行對接,這保持了Redis內部單線程設計的簡單性unix
文件事件處理器的構成server
上圖展現了文件事件處理器的四個組成部分,它們分別是套接字、I/O多路複用程序、文件事件分派器,以及事件處理器隊列
文件事件是對套接字操做的抽象、每當一個套接字準備好執行鏈接應答(accept)、寫入、讀取、關閉等操做時,就會產生一個文件事件。覺得一個服務器一般會鏈接多個套接字,因此多個文件事件有可能會併發地出現。I/O多路複用程序負責監聽多個套接字,並向文件事件分派器傳送那些產生了事件的套接字,儘管多個文件事件可能會併發地出現,但I/O多路複用程序老是會將全部產生事件的套接字都放到一個隊列裏面,而後經過這個隊列,以有序、同步、每次一個套接字的方式向文件事件分派器傳送套接字。當上一個套接字產生的事件被處理完畢以後,I/O多路複用程序纔會繼續向文件事件分派器傳送下一個套接字。
文件事件分派器接收I/O多路複用程序傳來的套接字,並根據套接字產生的事件的類型,調用相應的事件處理器。服務器會爲執行不一樣任務的套接字關聯不一樣的事件處理器,這些處理器是一個個函數。
I/O多路複用程序的實現
Redis的I/O多路複用程序的全部功能都是經過包裝常見的select,epoll,evport和kqueue這些I/O多路複用函數庫來實現的。由於Redis爲每一個I/O多路複用函數庫都實現了相同的API,因此I/O多路複用程序的底層實現是能夠互換的
事件的類型
I/O多路複用程序能夠監聽多個套接字的AE_READABLE事件和AE_WRITABLE事件,這兩類事件和套接字操做之間的對應關係以下
1:當套接字變得可讀時(客戶端對套接字執行write操做,或者執行close操做),或者有新的可應答(acceptable)套接字出現時(客戶端對服務器的監聽套接字執行connect操做),套接字產生AE_READABLE事件
2:當套接字變得可寫時(客戶端對套接字執行read操做),套接字產生AE_WRITABLE事件
I/O多路複用程序容許服務器同時監聽套接字的AE_READBLE事件和AE_WRITABLE事件,若是一個套接字同時產生了這兩種事件,那麼文件事件分派器會優先處理AE_READABLE事件,等到AE_READBALE事件處理完以後,才處理AE_WRITABLE事件。若是一個套接字又可讀又可寫的話,那麼服務器將先讀套接字,後寫套接字。
若是套接字沒有任何事件被監聽,那麼函數返回AE_NONE
若是套接字的讀事件正在被監聽,那麼函數返回AE_READABLE
若是套接字的寫事件正在被監聽,那麼函數返回AE_WRITABLE
若是套接字的讀事件和寫事件正在被監聽,那麼函數返回AE_REABALE|AE_WRITABLE
文件事件的處理器
1:爲了對鏈接服務器的各個客戶端進行應答、服務器要爲監聽套接字關聯鏈接應答處理器
2:爲了接收客戶端傳來的命令請求,服務器要爲客戶端套接字關聯請求處理器
3:爲了向客戶端返回命令的執行結果,服務器要爲客戶端套接字關聯命令回覆處理器
4:當主服務器和從服務器進行復制操做時,主從服務器都須要關聯特別爲複製功能編寫的複製處理器
鏈接應答處理器
當有客戶端用connect函數鏈接服務器監聽套接字的時候,套接字就會產生AE_REABELE事件,引起鏈接應答處理器執行,並執行相應的套接字應答操做
命令請求處理器
當一個客戶端經過鏈接應答處理器成功鏈接到服務器以後,服務器會將客戶端套接字的AE_READABLE事件和命令請求處理器關聯起來,當客戶端向服務器發送命令請求的時候,套接字就會產生AE_READABLE事件,引起命令請求處理器執行,並執行相應的套接字讀入操做,在客戶端鏈接服務器的整個過程當中,服務器都會一直爲客戶端套接字的AE_READABLE事件關聯命令請求處理器
命令回覆處理器
當服務器有命令回覆須要傳送給客戶端的時候,服務器會將客戶端套接字的AE_WRITABLE事件和命令回覆處理器關聯起來,當客戶端準備好接收服務器傳回的命令回覆時,就會產生AE_WRITABLE事件,引起命令回覆處理器執行,並執行相應的套接字寫入操做
一次完整的客戶端與服務器鏈接事件示例
假設一個Redis服務器正在運做,那麼這個服務器的監聽套接字的AE_READABLE事件應該正處於監聽狀態之下,而該事件所對應的處理器爲鏈接應答處理器
若是這時有一個Redis客戶端向服務器發起鏈接,那麼監聽套接字將產生AE_READABLE事件,觸發鏈接應答處理器執行。處理器會對客戶端的鏈接請求進行應答,而後建立客戶端套接字,以及客戶端狀態,並將客戶端套接字的AE_READBALE事件與命令請求處理器進行關聯,使得客戶端能夠向主服務器發送命令請求
以後假設客戶端向主服務器發送一個命令請求,那麼客戶端套接字將產生AE_READABLE事件,引起命令請求處理器執行,處理器讀取客戶端得命令內容,而後傳給相關程序去執行
執行命令將產生相應得命令回覆,爲了將這些命令回覆傳送回客戶端,服務器會將客戶端套接字得AE_WRITABLE事件與命令回覆處理器進行關聯。當客戶端嘗試讀取命令回覆的時候,客戶端套接字將產生AE_WRITABLE事件,觸發命令回覆處理器執行,當命令回覆處理器將命令回覆所有寫入到套接字以後,服務器就會解除客戶端套接字的AE_WRITABLE事件與命令回覆處理器之間的關聯。
時間事件
Redis的時間事件分爲如下兩類:
1:定時事件:讓一段程序在指定的時間以後執行一次
2:週期性事件:讓一段程序每隔指定時間就執行一次
一個時間事件主要由如下三個屬性組成
id:服務器爲時間事件建立的全局惟一ID。ID號按從小到大的順序遞增,新事件的ID號比舊事件的ID號要大
when:毫秒精度的unix時間戳,記錄了時間事件的到達時間
timeProc:時間事件處理器,一個函數。當時間事件到達時,服務器就會調用相應的處理器來處理事件
一個時間事件是定時事件仍是週期事件取決於時間事件處理器的返回值:
1:若是事件處理器返回AE_NOMORE,那麼這個事件爲定時事件:該事件在達到一次以後就會被刪除,以後再也不到達
2:若是事件處理器返回一個非AE_NOMORE的整數值,那麼這個事件爲週期性時間:當一個時間事件到達後,服務器會根據事件處理器返回的值,對時間事件的when屬性進行更新
讓這個事件在一段時間以後再次到達,並以這種方式一直更新並運行下去。
服務器將全部時間事件都放在一個無序鏈表中,每當時間事件執行器運行時,它就遍歷整個鏈表,查找全部已到達的時間事件,並調用相應的事件處理器
事件的調度與執行
由於服務器中同時存在文件事件和時間事件兩種事件類型,因此服務器必須對這兩種事件進行調度,決定什麼時候應該處理文件事件,什麼時候又應該處理時間事件,以及花多少時間來處理它們等等