Netty源碼學習(2)-- Reactor模式

網絡服務的基本服務:

讀請求--> 解碼請求 --> 處理服務器 --> 響應編碼 --> 發送響應


傳統的服務設計:

Netty源碼學習(2)-- Reactor模式

每一個handler需要一個線程進行處理。所以當連接數特別大的時候,傳統的方式會非常耗費資源。

Divide and Conquer

一種新的設計思路是將這一個大的任務變成多個小的任務,而Java中支持兩種機制:Non-blocking讀寫和dispatch任務分發。所以我們可以考慮將一個大任務分解成多個非阻塞事件。比如我們可以使用read handler和write handler來處理讀寫事件,這樣無論多少個鏈接,讀寫操作可以只用兩個線程進行執行。


Reactor模式

reactor:通過分發給合適的處理器來響應IO事件,類似於AWT中的Thread(本質就是線程,netty中的EventLoop)

handlers:用來處理非阻塞操作,類似於AWT中的ActionListeners

管理:通過將handler綁定在事件上,類似於AWT中的addListener

Netty源碼學習(2)-- Reactor模式

單線程react版本:

reactor這個線程不斷調用selector.select()方法,當拿到一個準備好的事件key之後(SelectionKey),通過調用key.attachment()拿到這個事件上對應的一個runnable對象(以Acceptor為例), 調用acceptor方法,acceptor中的run方法將socket和channel傳遞給一個handler,由handler自己進行處理。

問題:單線程情況下worker的阻塞會直接導致reactor分發變慢,同時各個worker之間也會有相互影響。考慮將非IO處理移交給其他線程。對於reactor也能採用多線程模式。


多線程版本:

Netty源碼學習(2)-- Reactor模式

在分發至handler中,handler通過線程池拿到線程進行處理。


多Reactor版本:

Netty源碼學習(2)-- Reactor模式

mainReactor就是netty中的bossGroup,subReactor就是workerGroup,main只將事件分發給不同的sub,每個sub再鏈接相應的事件。


Reactor模擬的構成:

  1. handle(句柄描述符):本質上是一種資源,表示一個個事件,如文件描述符,針對網絡編程中的socket描述符。事件既可以來自於外部,也可以來自於內部。linux下就是一個文件描述符。handle是事件產生的發源地。
  2. Synchronous Event Demultiplexer: 本身是一個系統調用,用於等待事件的發生,可能是一個,也可能是多個。調用時被阻塞,有事件才會繼續。Linux中的select,poll,和epoll就是這種模式。對應NIO中的selector。
  3. EventHanlder: 本身由多個回調方法構成,這些回調方法構成了對某個事件的反饋。NIO中沒有這個角色,但是netty進行了改進。
  4. Concrete Event handler:具體的事件處理器,用戶一般繼承eventHandler,實現回調方法,實現自己的功能。
  5. Initiation Dispatcher:就是eventLoop(Reactor),它定義了一些規範,規範用於控制事件的調度方式,同時又提供了應用進行事件處理的註冊、刪除等措施。它等待Synchronous Event Demultiplexer事件的發生,事件發生後,它會分離每一個事件,然後得到handler,調用其中的回調方法。

流程:

首先Initiation Dispatcher啟動,並將handler進行註冊,每個handler都會帶一個感興趣的事件handle。註冊完畢後Synchronous Event Dispatcher啟動,監聽事件發生(select)。事件發生後拿到對應的handle,然後找到具體handler,調用其中對應的回調方法。


詳解:

  1. 當應用向Initiation Dispatcher註冊具體的事件處理器時(註冊handler),同時回標出發生哪個事件時會調用這個handler,同時事件會和handle相關聯。
  2. Initiation Dispatcher會要求處理器傳遞關聯的handle。該handle向操作系統標識除了事件處理器。
  3. 當事件處理器註冊完畢後,會調用handle_events方法來啟動Initiation Dispatcher的事件循環。這時,Initiation Dispatcher會將每個事件管理器的handle合併起來,同時等待事件的發生。
  4. 當事件ready時,Synchronous Event Demultiplexer會通知Initiation Dispatcher。
  5. Initiation Dispatcher觸發事件的回調方法,相應這個handle,拿到對應的handler,調用回調方法。


以客戶都鏈接服務器,服務器打印客戶端ip為例。首先新建一個Socket監聽端口8899,這裡的socket就是一個handle。接下來,我們新建一個handler,裡面有一個函數接受handle(這裡就是socket)作為參數,然後打印這個socket對應的ip。我們將handle和我們自己的handler進行關聯,同時將handle註冊到Initiation Dispatcher中,同時標註我們對connect事件感興趣。然後Synchronous Event Demultiplexer會不斷循環,知道有一個客戶端進行了鏈接,連接之後我們Synchronous Event Demultiplexer通知Initiation Dispatcher這個事件(handle)ready了,同時因為這個handle有對應的handler,我們拿到handler,調用其中的方法,打印socket的ip即可。

本文出自知乎

Netty源碼學習(2)-- Reactor模式

更多相關內容,Java架構師,軟件開發等學習資料,電子書及視頻還有高級講師公開課免費資源

需要的可以私聊小編髮送【學習】二字


分享到:


相關文章: