概述
- 在使用JAVA NIO和多線程來進行高并發Java服務端應用程序設計時,通常是基于Reactor線程模型來設計的。Reactor,即包含一個Java NIO的多路復用選擇器Selector的反應堆,當有反應時,即該Selector所管理的某個客戶端連接有IO事件過來時,則在當前線程或者分配到其他線程來處理該IO事件。
- Reactor線程模型通常由接收客戶端連接請求的acceptor線程和處理客戶端的IO請求的IO線程兩部分組成,而acceptor線程和IO處理線程可以是同一個線程,也可以是不同的線程或者是各自對應一個線程池。
單線程Reactor模型
- 單線程Reactor模型是指由一個線程綁定一個Java NIO的多路復用選擇器Selector,由該線程來處理該Selector的所有IO事件,包括新客戶端連接建立請求,即監聽套接字的IO事件,和已經建立連接的客戶端套接字的所有IO事件請求,如數據讀寫。在單線程Reactor模型中,Reactor模型中的新客戶端連接建立的acceptor線程和已經建立連接的客戶端套接字的IO請求處理的IO線程是同一個線程。
- 具體工作模式如圖:服務端使用一個線程來處理新客戶端的連接請求和已建立連接的客戶端的讀寫IO事件。

- 由于該Selector所管理的所有客戶端連接和服務端的監聽套接字的IO請求都是由該線程來處理,所以如果當前線程正在處理某個客戶端的數據讀寫IO請求,則無法處理當前的新建立連接的客戶端請求,導致客戶端無法建立連接或者連接超時。同時由于只使用一個線程所有也沒有充分利用多核CPU。
- 所以雖然通過使用單線程,規避了線程競爭和線程上下文切換,但是由于單個線程處理能力有限,所以單線程Reactor模型也無法支撐高并發客戶端請求的場景,Java服務端應用程序設計很少使用到該模型。
單Acceptor線程多IO線程的Reactor模型
- 多線程Reactor模型與單線程Reactor模型的主要區別是已經建立連接的客戶端套接字的IO事件是在另外一個線程或者另外一個線程池來處理的,這種線程也稱為IO線程,而處理監聽套接字的新客戶端連接請求的線程則還是一個獨立的Acceptor線程。
- 具體工作過程如圖所示:使用在一個獨立Acceptor線程通過監聽套接字監聽客戶端的連接請求,當有新的客戶端連接到來時,創建該客戶端對應的channel并注冊到其他一個IO線程的Selector,由該Selector監視和獲取該客戶端channel后續的讀寫IO事件并進行處理。

- 以上示意圖是針對一個已建立連接的客戶端套接字的所有IO請求可以是始終在IO線程池中的一個IO線程中處理。不過也可以是使用一個專門的IO線程來負責監視所有客戶端的channels,當某個客戶端有IO事件到來時,將該客戶端的此次IO事件封裝為一個Runnable的任務,交給另外的線程池處理,即某個客戶端的所有IO事件會在不同中線程處理。不過推薦是始終在一個IO線程中處理,這樣就不存在多個線程對該客戶端套接字進行并發操作的場景,即不需要通過加鎖之類的操作來對該客戶端套接字對應的channel對象引用進行線程同步。
- 這種已建立連接的客戶端的所有IO事件都是在同一個IO線程中處理的方式也是netty4的IO線程模型,通過客戶端channel與線程的綁定來避免線程競爭來提高性能。
多Acceptor線程多IO線程Reactor模型
- 多Acceptor線程多IO線程Reactor模型實際與單Acceptor線程多IO線程Reactor模型差不多,主要一個區別就是用于接收客戶端的連接請求的線程不再是只使用一個acceptor線程,而是使用一個包含多個acceptor線程的線程池,從而解決單個acceptor線程在處理多個不同的端口的高并發客戶端連接建立請求時的性能瓶頸,即當服務端需要同時監聽多個端口時,可以使用一個包含多個Acceptor線程的線程池。
- 該模型的工作過程如圖所示:使用多個Acceptor線程來處理客戶端的連接請求。
