天天看點

為什麼說Netty是性能之王,因為它用了 Reactor 模型啊

  • 1、線程模型1:傳統阻塞 I/O 服務模型
  • 2、線程模型2:Reactor 模式
  • 2.1 基本介紹
  • 2.2 單 Reactor 單線程
  • 2.3單 Reactor 多線程
  • 2.4 主從 Reactor 多線程
  • 2.5 小結
  • 3、線程模型2:Proactor 模型

本文将介紹基于程序/線程模型,伺服器如何處理請求。值得說明的是,具體選擇線程還是程序,更多是與平台及程式設計語言相關。

例如 C 語言使用線程和程序都可以(例如 Nginx 使用程序,Memcached 使用線程),Java 語言一般使用線程(例如 Netty),為了描述友善,下面都使用線程來進行描述。

​​1、線程模型1:傳統阻塞 I/O 服務模型​​

為什麼說Netty是性能之王,因為它用了 Reactor 模型啊

圖檔

特點:

1)采用阻塞式 I/O 模型擷取輸入資料;

2)每個連接配接都需要獨立的線程完成資料輸入,業務處理,資料傳回的完整操作。

存在問題:

1)當并發數較大時,需要建立大量線程來處理連接配接,系統資源占用較大;

2)連接配接建立後,如果目前線程暫時沒有資料可讀,則線程就阻塞在 Read 操作上,造成線程資源浪費。

​​2、線程模型2:Reactor 模式​​

​​2.1 基本介紹​​

針對傳統阻塞 I/O 服務模型的 2 個缺點,比較常見的有如下解決方案:

1)基于 I/O 複用模型:多個連接配接共用一個阻塞對象,應用程式隻需要在一個阻塞對象上等待,無需阻塞等待所有連接配接。當某條連接配接有新的資料可以處理時,作業系統通知應用程式,線程從阻塞狀态傳回,開始進行業務處理;

2)基于線程池複用線程資源:不必再為每個連接配接建立線程,将連接配接完成後的業務處理任務配置設定給線程進行處理,一個線程可以處理多個連接配接的業務。

I/O 複用結合線程池,這就是 Reactor 模式基本設計思想,如下圖:

為什麼說Netty是性能之王,因為它用了 Reactor 模型啊

Reactor 模式,是指通過一個或多個輸入同時傳遞給服務處理器的服務請求的事件驅動處理模式。

服務端程式處理傳入多路請求,并将它們同步分派給請求對應的處理線程,Reactor 模式也叫 Dispatcher 模式。

即 I/O 多了複用統一監聽事件,收到事件後分發(Dispatch 給某程序),是編寫高性能網絡伺服器的必備技術之一。

Reactor 模式中有 2 個關鍵組成:

1)Reactor:Reactor 在一個單獨的線程中運作,負責監聽和分發事件,分發給适當的處理程式來對 IO 事件做出反應。它就像公司的電話接線員,它接聽來自客戶的電話并将線路轉移到适當的聯系人;

2)Handlers:處理程式執行 I/O 事件要完成的實際事件,類似于客戶想要與之交談的公司中的實際官員。Reactor 通過排程适當的處理程式來響應 I/O 事件,處理程式執行非阻塞操作。

根據 Reactor 的數量和處理資源池線程的數量不同,有 3 種典型的實作:

1)單 Reactor 單線程;2)單 Reactor 多線程;3)主從 Reactor 多線程。

下面詳細介紹這 3 種實作方式。

​​2.2 單 Reactor 單線程​​

為什麼說Netty是性能之王,因為它用了 Reactor 模型啊

其中,Select 是前面 I/O 複用模型介紹的标準網絡程式設計 API,可以實作應用程式通過一個阻塞對象監聽多路連接配接請求,其他方案示意圖類似。

方案說明:

1)Reactor 對象通過 Select 監控用戶端請求事件,收到事件後通過 Dispatch 進行分發;

2)如果是建立連接配接請求事件,則由 Acceptor 通過 Accept 處理連接配接請求,然後建立一個 Handler 對象處理連接配接完成後的後續業務處理;

3)如果不是建立連接配接事件,則 Reactor 會分發調用連接配接對應的 Handler 來響應;

4)Handler 會完成 Read→業務處理→Send 的完整業務流程。

優點:模型簡單,沒有多線程、程序通信、競争的問題,全部都在一個線程中完成。

缺點:性能問題,隻有一個線程,無法完全發揮多核 CPU 的性能。Handler 在處理某個連接配接上的業務時,整個程序無法處理其他連接配接事件,很容易導緻性能瓶頸。

可靠性問題,線程意外跑飛,或者進入死循環,會導緻整個系統通信子產品不可用,不能接收和處理外部消息,造成節點故障。

使用場景:用戶端的數量有限,業務處理非常快速,比如 Redis,業務處理的時間複雜度 O(1)。

​​2.3單 Reactor 多線程​​

為什麼說Netty是性能之王,因為它用了 Reactor 模型啊

2)如果是建立連接配接請求事件,則由 Acceptor 通過 Accept 處理連接配接請求,然後建立一個 Handler 對象處理連接配接完成後續的各種事件;

4)Handler 隻負責響應事件,不做具體業務處理,通過 Read 讀取資料後,會分發給後面的 Worker 線程池進行業務處理;

5)Worker 線程池會配置設定獨立的線程完成真正的業務處理,如何将響應結果發給 Handler 進行處理;

6)Handler 收到響應結果後通過 Send 将響應結果傳回給 Client。

優點:可以充分利用多核 CPU 的處理能力。

缺點:多線程資料共享和通路比較複雜;Reactor 承擔所有事件的監聽和響應,在單線程中運作,高并發場景下容易成為性能瓶頸。

​​2.4 主從 Reactor 多線程​​

為什麼說Netty是性能之王,因為它用了 Reactor 模型啊

針對單 Reactor 多線程模型中,Reactor 在單線程中運作,高并發場景下容易成為性能瓶頸,可以讓 Reactor 在多線程中運作。

1)Reactor 主線程 MainReactor 對象通過 Select 監控建立連接配接事件,收到事件後通過 Acceptor 接收,處理建立連接配接事件;

2)Acceptor 處理建立連接配接事件後,MainReactor 将連接配接配置設定 Reactor 子線程給 SubReactor 進行處理;

3)SubReactor 将連接配接加入連接配接隊列進行監聽,并建立一個 Handler 用于處理各種連接配接事件;

4)當有新的事件發生時,SubReactor 會調用連接配接對應的 Handler 進行響應;

5)Handler 通過 Read 讀取資料後,會分發給後面的 Worker 線程池進行業務處理;

6)Worker 線程池會配置設定獨立的線程完成真正的業務處理,如何将響應結果發給 Handler 進行處理;

7)Handler 收到響應結果後通過 Send 将響應結果傳回給 Client。

優點:父線程與子線程的資料互動簡單職責明确,父線程隻需要接收新連接配接,子線程完成後續的業務處理。

父線程與子線程的資料互動簡單,Reactor 主線程隻需要把新連接配接傳給子線程,子線程無需傳回資料。

這種模型在許多項目中廣泛使用,包括 Nginx 主從 Reactor 多程序模型,Memcached 主從多線程,Netty 主從多線程模型的支援。

​​2.5 小結​​

3種模式可以用個比喻來了解:(餐廳常常雇傭接待員負責迎接顧客,當顧客入坐後,侍應生專門為這張桌子服務)

1)單 Reactor 單線程,接待員和侍應生是同一個人,全程為顧客服務;

2)單 Reactor 多線程,1 個接待員,多個侍應生,接待員隻負責接待;

3)主從 Reactor 多線程,多個接待員,多個侍應生。

Reactor 模式具有如下的優點:

1)響應快,不必為單個同步時間所阻塞,雖然 Reactor 本身依然是同步的;

2)程式設計相對簡單,可以最大程度的避免複雜的多線程及同步問題,并且避免了多線程/程序的切換開銷;

3)可擴充性,可以友善的通過增加 Reactor 執行個體個數來充分利用 CPU 資源;

4)可複用性,Reactor 模型本身與具體事件處理邏輯無關,具有很高的複用性。

​​3、線程模型2:Proactor 模型​​

在 Reactor 模式中,Reactor 等待某個事件或者可應用或者操作的狀态發生(比如檔案描述符可讀寫,或者是 Socket 可讀寫)。

然後把這個事件傳給事先注冊的 Handler(事件處理函數或者回調函數),由後者來做實際的讀寫操作。

其中的讀寫操作都需要應用程式同步操作,是以 Reactor 是非阻塞同步網絡模型。

如果把 I/O 操作改為異步,即交給作業系統來完成就能進一步提升性能,這就是異步網絡模型 Proactor。

為什麼說Netty是性能之王,因為它用了 Reactor 模型啊

Proactor 是和異步 I/O 相關的,詳細方案如下:

1)Proactor Initiator 建立 Proactor 和 Handler 對象,并将 Proactor 和 Handler 都通過 AsyOptProcessor(Asynchronous Operation Processor)注冊到核心;

2)AsyOptProcessor 處理注冊請求,并處理 I/O 操作;

3)AsyOptProcessor 完成 I/O 操作後通知 Proactor;

4)Proactor 根據不同的事件類型回調不同的 Handler 進行業務處理;

5)Handler 完成業務處理。

可以看出 Proactor 和 Reactor 的差別:

1)Reactor 是在事件發生時就通知事先注冊的事件(讀寫在應用程式線程中處理完成);

2)Proactor 是在事件發生時基于異步 I/O 完成讀寫操作(由核心完成),待 I/O 操作完成後才回調應用程式的處理器來進行業務處理。

理論上 Proactor 比 Reactor 效率更高,異步 I/O 更加充分發揮 DMA(Direct Memory Access,直接記憶體存取)的優勢。

但是Proactor有如下缺點:

1)程式設計複雜性,由于異步操作流程的事件的初始化和事件完成在時間和空間上都是互相分離的,是以開發異步應用程式更加複雜。應用程式還可能因為反向的流控而變得更加難以 Debug;

繼續閱讀