深度解析Redis線程模型設計原理 1 單線程模型設計 2 文件事件處理器 3 總結

全是乾貨的技術號:
本文已收錄在github,歡迎 star/fork:
https://github.com/Wasabi1234/Java-Interview-Tutorial

1 單線程模型設計

單線程模型爲何效率高

  • 純內存操作
  • 基於非阻塞的IO多路複用機制
  • 避免了多線程的頻繁上下文切換

2 文件事件處理器

Redis 基於 Reactor 模式開發了自己的網絡事件處理器 - 文件事件處理器(file event handler,後文簡稱爲 FEH),而該處理器又是單線程的,所以redis設計爲單線程模型。

  • 採用I/O多路複用同時監聽多個socket,根據socket當前執行的事件來爲 socket 選擇對應的事件處理器。
  • 當被監聽的socket準備好執行acceptreadwriteclose等操作時,和操作對應的文件事件就會產生,這時FEH就會調用socket之前關聯好的事件處理器來處理對應事件。

所以雖然FEH是單線程運行,但通過I/O多路複用監聽多個socket,不僅實現高性能的網絡通信模型,又能和 Redis 服務器中其它同樣單線程運行的模塊交互,保證了Redis內部單線程模型的簡潔設計。

  • 下面講講文件事件處理器的幾個組成部分。


2.1 socket

文件事件就是對socket操作的抽象, 每當一個 socket 準備好執行連接accept、read、write、close等操作時, 就會產生一個文件事件。 一個服務器通常會連接多個socket, 多個socket可能併發產生不同操作,每個操作對應不同文件事件。

2.2 I/O多路複用程序

I/O 多路複用程序會負責監聽多個socket。

儘管文件事件可能併發出現, 但 I/O 多路複用程序會將所有產生事件的socket放入隊列, 通過該隊列以有序、同步且每次一個socket的方式向文件事件分派器傳送socket。
當上一個socket產生的事件被對應事件處理器執行完後, I/O 多路複用程序纔會向文件事件分派器傳送下個socket, 如下:


I/O多路複用程序的實現

Redis 的 I/O 多路複用程序的所有功能都是通過包裝常見的 select 、 epoll 、 evport 和 kqueue 這些 I/O 多路複用函數庫實現的。
每個 I/O 多路複用函數庫在 Redis 源碼中都對應一個單獨的文件:
[圖片上傳失敗...(image-c54286-1598968387613)]

因爲 Redis 爲每個 I/O 多路複用函數庫都實現了相同的 API , 所以 I/O 多路複用程序的底層實現是可以互換的。Redis 在 I/O 多路複用程序的實現源碼ae.c文件中宏定義了相應規則,使得程序在編譯時自動選擇系統中性能最高的 I/O 多路複用函數庫作爲 Redis 的 I/O 多路複用程序的底層實現:性能降序排列。

2.3 文件事件分派器

文件事件分派器接收 I/O 多路複用程序傳來的socket, 並根據socket產生的事件類型, 調用相應的事件處理器。

2.4 文件事件處理器

服務器會爲執行不同任務的套接字關聯不同的事件處理器, 這些處理器是一個個函數, 它們定義了某個事件發生時, 服務器應該執行的動作。

處理器映射

Redis 爲各種文件事件需求編寫了多個處理器,若客戶端:

  • 連接Redis,對連接服務器的各個客戶端進行應答,就需要將socket映射到連接應答處理器
  • 寫數據到Redis,接收客戶端傳來的命令請求,就需要映射到命令請求處理器
  • 從Redis讀數據,向客戶端返回命令的執行結果,就需要映射到命令回覆處理器

當主服務器和從服務器進行復制操作時, 主從服務器都需要映射到特別爲複製功能編寫的複製處理器。

2.5 文件事件的類型

I/O 多路複用程序可以監聽多個socket的 ae.h/AE_READABLE 事件和 ae.h/AE_WRITABLE 事件, 這兩類事件和套接字操作之間的對應關係如下:

  • 當socket可讀(比如客戶端對Redis執行write/close操作),或有新的可應答的socket出現時(即客戶端對Redis執行connect操作),socket就會產生一個AE_READABLE事件
    [圖片上傳失敗...(image-77efd2-1598968387613)]
  • 當socket可寫時(比如客戶端對Redis執行read操作),socket會產生一個AE_WRITABLE事件。
    [圖片上傳失敗...(image-6ea1a3-1598968387613)]

I/O多路複用程序可以同時監聽AE_REABLEAE_WRITABLE兩種事件,要是一個socket同時產生這兩種事件,那麼文件事件分派器優先處理AE_REABLE事件。即一個socket又可讀又可寫時, Redis服務器先讀後寫socket。

3 總結

最後,讓我們梳理一下客戶端和Redis服務器通信的整個過程:


  1. Redis啓動初始化時,將連接應答處理器AE_READABLE事件關聯。
  2. 若一個客戶端發起連接,會產生一個AE_READABLE事件,然後由連接應答處理器負責和客戶端建立連接,創建客戶端對應的socket,同時將這個socket的AE_READABLE事件和命令請求處理器關聯,使得客戶端可以向主服務器發送命令請求。
  3. 當客戶端向Redis發請求時(不管讀還是寫請求),客戶端socket都會產生一個AE_READABLE事件,觸發命令請求處理器。處理器讀取客戶端的命令內容, 然後傳給相關程序執行。
  4. 當Redis服務器準備好給客戶端的響應數據後,會將socket的AE_WRITABLE事件和命令回覆處理器關聯,當客戶端準備好讀取響應數據時,會在socket產生一個AE_WRITABLE事件,由對應命令回覆處理器處理,即將準備好的響應數據寫入socket,供客戶端讀取。
  5. 命令回覆處理器全部寫完到 socket 後,就會刪除該socket的AE_WRITABLE事件和命令回覆處理器的映射。

參考

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