Redis教程:事件、客戶端和服務器

1 事件

Redis服務器是一個事件驅動程序,服務器需要處理以下兩類事情:

  • 文件事件(file event):Redis服務器通過套接字與客戶端(或者其他Redis服務器)進行連接,而文件事件就是服務器對套接字操作的抽象。服務器與客戶端(或者其他服務器)的通信會產生相應的文件事件,而服務器則通過監聽並處理這些事件來完成一系列網絡通信操作
  • 時間事件(time event):Redis服務器中的一些操作(比如serverCron函數)需要在給定的時間點執行,而時間事件就是服務器對這類定時操作的抽象

1.1 文件事件

Redis是基於Reactor模式開發了自己的網絡事件處理器,這個處理器被稱爲文件事件處理器:

  • 文件事件處理器使用I/O多路複用程序來同時監聽多個套接字,並根據套接字目前執行的任務來爲套接字關聯不同的事件處理器
  • 當被監聽的套接字準備好執行連接應答(accept)、讀取(read)、寫入(write)、關閉(close)等操作時,與操作相對應的文件事件就會產生,這時文件事件處理器就會調用套接字之前關聯好的事件處理器來處理這些事件

       雖然文件事件處理器以單線程方式運行,但通過使用I/O多路複用程序來監聽多個套接字,文件事件處理器既實現了高性能的網絡通信模型,又可以很好地與Redis服務器中其他同樣以單線程方式運行的模塊進行對接,這保持了Redis內部單線程設計的簡單性。

1.2 時間事件

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

  • 定時事件:讓一段程序在指定的時間之後執行一次。比如說,讓程序X在當前時間的30毫秒之後執行一次
  • 週期性事件:讓一段程序每隔指定時間就執行一次。比如說,讓程序Y每隔30毫秒就執行一次

一個時間事件主要由以下三個屬性組成:

  • id:服務器爲時間事件創建的全局唯一ID(標識號)。ID號按從小到大的順序遞增,新事件的ID號比舊事件的ID號要大
  • when:毫秒精度的UNIX時間戳,記錄了時間事件的到達(arrive)時間
  • timeProc:時間事件處理器,一個函數。當時間事件到達時,服務器就會調用相應的處理器來處理事件

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

  • 如果事件處理器返回ae.h/AE_NOMORE,那麼這個事件爲定時事件:該事件在達到一次之後就會被刪除,之後不再到達
  • 如果事件處理器返回一個非AE_NOMORE的整數值,那麼這個事件爲週期性時間:當一個時間事件到達之後,服務器會根據事件處理器返回的值,對時間事件的when屬性進行更新,讓這個事件在一段時間之後再次到達,並以這種方式一直更新並運行下去。比如說,如果一個時間事件的處理器返回整數值30,那麼服務器應該對這個時間事件進行更新,讓這個事件在30毫秒之後再次到達

2 客戶端

        Redis服務器狀態結構的clients屬性是一個鏈表,這個鏈表保存了所有與服務器連接的客戶端的狀態結構,對客戶端執行批量操作,或者查找某個指定的客戶端,都可以通過遍歷clients鏈表來完成:

redis.h

struct redisServer {
    ……
    //一個鏈表,保存了所有客戶端狀態
    list *clients;            
    ……
};

2.1 客戶端的創建和關閉

服務器使用不同的方式來創建和關閉不同類型的客戶端,將介紹服務器創建和關閉客戶端的方法。

創建普通客戶端

      如果客戶端是通過網絡連接和服務器進行連接的普通客戶端,那麼在客戶端使用connect函數連接到服務端時,服務器就會調用連接事件處理器,爲客戶端創建相應的客戶端狀態,並將這個客戶端狀態添加到服務器狀態結構體中clients鏈表的末尾。

      舉個栗子,假設當前有c1和c2兩個普通客戶端正在連接服務器,那麼當一個新的普通客戶端c3連接到服務器後,服務器會將c3所對應的客戶端狀態添加到clients鏈表的末尾,如圖2-1所示,其中用虛線包圍的就是服務器爲c3新創建的客戶端狀態。

圖2-1  服務器狀態結構體的clients鏈表

關閉普通客戶端

一個普通客戶端可以因爲多種原因而被關閉:

  •  如果客戶端進程退出或者被殺死,那麼客戶端與服務器之間的網絡連接將被關閉,從而造成客戶端被關閉
  • 如果客戶端向服務器發送了帶有不符合協議格式的命令請求,那麼這個客戶端也會被服務器關閉
  • 如果客戶端成爲了CLIENTKILL命令的目標,那麼它也會被關閉
  • 如果用戶爲服務器設置了timeout配置選項,那麼當客戶端的空轉時間超過timeout選項設置的值時,客戶端將被關閉。不過timeout選項有一些例外情況:如果客戶端是主服務器(打開了REDIS_MASTER標誌),從服務器(打開了REDIS_SLAVE標誌),正在被BLPOP等命令阻塞(打開了REDIS_BLOCKED標誌),或者正在執行SUBSCRIBE, PSUBSCRIBE等訂閱命令,那麼即使客戶端的空轉時間超過了timeout選項的值,客戶端也不會被服務器關閉
  • 如果客戶端發送的命令請求的大小超過了輸入緩衝區的限制大小(默認爲1GB),那麼這個客戶端會被服務器關閉
  • 如果要發送給客戶端的命令回覆的大小超過了輸出緩衝區的限制大小,那麼這個客戶端會被服務器關閉

       前面介紹輸出緩衝區的時候提到過,可變大小緩衝區由一個鏈表和任意多個字符串對象組成,理論上來說,這個緩衝區可以保存任意長度的命令回覆。但爲了避免客戶端回覆過大,佔用過多服務器資源,服務器會時刻檢查客戶端的輸出緩衝區的大小,並在緩衝區的大小超出範圍時,執行相應的限制操作。服務器使用兩種模式來限制客戶端輸出緩衝的大小:

  • 硬性限制(hard limit):如果輸出緩衝區的大小超過了硬性限制所設置的大小,那麼服務器立即關閉客戶端
  • 軟性限制(soft limit):如果輸出緩衝區的大小超過了軟性限制所設置的大小,但還沒有超過硬性限制,那麼服務器將使用服務器狀態結構的 obuf_soft_limit_reached_time 屬性記錄下客戶端到達軟性限制的起始時間,之後服務器會繼續監視客戶端,如果輸出緩衝區的大小一直超出軟性限制,並且持續時間超過服務器設定的時長,那麼服務器就會關閉客戶端,相反地,如果客戶端在指 定時間內不再超出軟性限制,那麼客戶端就不會被關閉,並且obuf_soft_limit_reached_time也會被清零

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章