redis服務器是一個事件驅動程序。
需要處理兩類事件:
1)文件事件:redis是通過套接字與客戶端或者其他服務器連接的,而文件事件就是服務器對套接字操作的抽象。
2)時間事件:服務器對一些定時操作的抽象。
文件事件
redis基于reactor模式開發了自己的網絡事件處理器,這個處理器被稱作文件事件處理器,它使用IO多路復用程序來同時監聽多個套接字, 并根據套接字目前執行的任務來為套接字關聯不同的事件處理器,當被監聽的套接字準備好執行連接應答(accept)、讀取(read)、寫入(write)、關閉(close)等操作時, 與操作相對應的文件事件就會產生, 這時文件事件處理器就會調用套接字之前關聯好的事件處理器來處理這些事件。
雖然文件事件處理器以單線程方式運行, 但通過使用 I/O 多路復用程序來監聽多個套接字, 文件事件處理器既實現了高性能的網絡通信模型, 又可以很好地與 Redis 服務器中其他同樣以單線程方式運行的模塊進行對接, 這保持了 Redis 內部單線程設計的簡單性。
文件事件處理器的構成:
I/O 多路復用程序負責監聽多個套接字, 并向文件事件分派器傳送那些產生了事件的套接字。
?I/O 多路復用程序會把所有產生事件的套接字放到一個隊列, 以有序(sequentially)、同步(synchronously)、每次一個套接字的方式,向文件事件分派器傳送套接字。
I/O 多路復用程序可以監聽多個套接字的?ae.h/AE_READABLE
?事件和?ae.h/AE_WRITABLE
?事件
1)當套接字變得可讀時(客戶端對套接字執行?write
?操作,或者執行?close
?操作), 或者有新的可應答(acceptable)套接字出現時(客戶端對服務器的監聽套接字執行?connect
?操作), 套接字產生?AE_READABLE
?事件。
2)當套接字變得可寫時(客戶端對套接字執行?read
?操作), 套接字產生?AE_WRITABLE
?事件。
如果一個套接字又可讀又可寫的話, 那么服務器將先讀套接字, 后寫套接字。
下面介紹各種處理器:
1)連接應答處理器:服務器進行初始化時, 程序會將連接應答處理器和服務器監聽套接字的?AE_READABLE
?事件關聯, 當有客戶端連接(connect
)服務器監聽套接字的時候, 套接字就會產生?AE_READABLE
?事件, 引發連接應答處理器執行, 并執行相應的套接字應答操作。
2)命令請求處理器:客戶端連接到服務器后, 服務器會將客戶端套接字的?AE_READABLE
?事件和命令請求處理器關聯起來, 當客戶端發送命令請求時, 套接字就會產生?AE_READABLE
?事件, 引發命令請求處理器執行, 并執行相應的套接字讀入操作
3)命令回復處理器:服務器有命令回復需要傳送給客戶端, 服務器會將客戶端套接字的?AE_WRITABLE
?事件和命令回復處理器關聯起來, 當客戶端準備好接收服務器傳回的命令回復時, 就會產生?AE_WRITABLE
?事件, 引發命令回復處理器執行, 并執行相應的套接字寫入操作。
一次完整的連接事件實例:
時間事件
redis時間事件可以分為兩類:定時事件、周期性事件,他們的特點就像他們的名字一樣。
而一個時間事件主要有三部分:
id:服務器為時間事件創建的全局唯一id,按時間遞增,越新的越大
when:unix時間戳,記錄到達時間
timeProc:時間事件處理器,是一個函數,時間事件到達時,服務器就會調用處理器來處理事件。
目前版本的redis只使用周期性事件
來看看實現:
服務器把所有時間事件放在一個鏈表中,每當時間事件執行器執行時,它就遍歷鏈表,調用相應的事件處理器。
但是注意:鏈表是無序的,不按when屬性來排序,當時間事件執行器運行時,必須遍歷整個鏈表。但是,無序鏈表并不影響時間事件處理器的性能,因為在目前版本中,redis服務器只使用serverCron一個時間事件,就算在benchmark模式下也只有兩個事件,服務器幾乎是把鏈表退化成指針使用了。
?
事件的調度和執行
?
文件事件和時間事件之間是合作關系, 服務器會輪流處理這兩種事件,對兩種事件的處理都是同步、有序、原子地進行的,處理事件的過程中也不會進行搶占,所以時間事件的實際處理時間通常會比設定的到達時間晚一些。
大概流程為:
是否關閉服務器?---->等待文件事件產生---->處理已經產生的文件事件---->處理已經達到的時間事件---->是否關閉服務器?........