這一節和我一起開始正式的去研究Netty原始碼。在研究之前,我想先介紹一下Reactor模型。
我先分享兩篇文獻,大家可以自行下載學習。 連結:https://pan.baidu.com/s/1Utym7AS8DHCxrh5aNOD2Ew 提取碼:18u3
下面主要是對文獻中一些內容和重要的圖片進行說明:
1、Each handler may be started in its own thread, 這種方式在客戶端負載增加的時候,有很嚴重的效能問題。
- serversocket的accept方法,阻塞等待client連線,直到client連線成功。
- 執行緒從socket inputstream outputstream讀寫資料,會進入阻塞狀態,直到全部資料讀寫完。
- 即使沒有資料的讀寫,仍然會佔用一個執行緒,浪費資源。
2、對上面的模型進行優化, 採用事件驅動的模式,有事件發生的時候才會觸發:
Reactor為單個執行緒,既要處理客戶端的accept事件,又要負責傳送請求到處理器中。由於只有單個執行緒,所以處理器中的業務需要能夠快速處理完。
3、再次進行改進,加入執行緒池
這樣Reactor執行緒處理完連線之後,將請求放線上程池中進行處理,不會受到Handler的阻塞。
4、使用多個Reactor
mainReactor負責監聽連線,accept連線給subReactor處理,為什麼要單獨分一個Reactor來處理監聽呢?因為像TCP這樣需要經過3次握手才能建立連線,這個建立連線的過程也是要耗時間和資源的,單獨分一個Reactor來處理,可以提高效能。mainReactor僅僅只負責處理客戶端連線,subReactor負責後面的資料的讀寫。
在解決了什麼是Reactor模式後,我們來看看Reactor模式是由什麼模組構成。圖是一種比較簡潔形象的表現方式,因而先上一張圖來表達各個模組的名稱和他們之間的關係:
上面的圖由五大角色構成,下面進行解釋:
- Handle(控制程式碼或描述符):本質上是一種資源,由作業系統提供的,該資源用於表示一個個的事件,比如說檔案描述符,或是針對網路程式設計中的Socket描述符,事件既可以來自於外部,又可以來自於內部,外部事件比如客戶端的連線請求,客戶端資料的讀寫等等;內部事件比如作業系統產生的定時器事件,它本質上就是一個檔案描述符。簡單來說,Handle就是事件產生的發源地。
- Synchronous Event Demultiplexer(同步事件分離器):它本身是一個系統呼叫,用於等待事件的發生(一個或多個)。呼叫方在呼叫它的時候會一直阻塞,一直阻塞到同步分離器上有事件產生,對於Linux來說,同步事件分離器指的就是常用的I/O多路複用器,比如說select、poll、epoll等,在Java NIO 領域中,同步事件分離器的元件就是Selector; 對應的阻塞方法就是select()
- Event Hander (事件處理器):本身由多個回撥方法組成,這些回撥方法構成了於應用相關的對於某個事件的反饋機制,Netty相比於Java的NIO來說,在事件處理器的這個角色上進行了一個升級,它為我們開發者指定了大量的回撥方法,供我們在特定時間產生的時候實現相應的回撥方法進行業務邏輯的處理。
- Concrete Event Handler(具體事件處理器):是事件處理器的時間,本質上是我們所編寫的一個個的處理器的實現
- Initiation Dispatcher (初始分發器):實際上就是Reactor的角色,它本身定義了一些規範,這些規範使用者控制事件的排程方式,同時又提供了應用事件處理器的註冊、刪除等設施,它本身是整個事件處理器的核心所在,會通過同步事件分離器來等待事件的發生,一旦事件發生,首先會分離出一個事件,然後呼叫事件處理器,最後呼叫相關的回撥方法來處理這些事件。