Netty快速入門(10)Reactor與Netty


Reactor模式

Reactor是1995年由道格拉斯提出的一種高性能網絡編程模式。由於好多年了,當時的一些概念與現在略有不同,reactor模式在網絡編程中是非常重要的,可以說是NIO框架的典型模式,一些經典的框架,比如Mina、Netty、Cindy都是此模式的實現。

我們來看看當年提出的通用模型:

file

上面的圖形中:

1、Handle 可以理解為資源或者文件句柄,放在netty里面就是channel,就是我們實際要處理的東西

2、Event Handler和Concrete Event Handler 就是具體的事件處理器,對應netty中的handler接口和具體的handler

3、Synchronous Event Demultiplexer同步事件多路復用分發器,可以理解為nio中的select

4、Initiation Dispatcher,分發器,可以理解為nio中的循環,也就是netty中的EventLoop,處理各種事件

5、select(handlers)就是真正處理業務的地方

大家注意上面圖形中的幾個箭頭,可以看出各個組件之間的關聯關系。這個經典的模型當時並不是針對Java提出的,任何擁有這些組件的語言,都可以實現高性能的reactor模型。

單線程Reactor

基於Java,Doug Lea(Java並發包作者)提出了三種形式,單線程Reactor,多線程Reactor和Multiple Reactor。首先看一下基本的單線程模式:

file

單線程模型就是一個reactor(select+循環),客戶端(client)注冊進來由reactor接收注冊事件,然后再由reactor分發(dispatch)出去,由下面的處理器(read、decode、compute等)去處理。我們前面復習nio代碼的時候,程序就是這樣的結構,只有一個select。

多線程Reactor

單線程的Reactor有明顯的不足,只有一個線程,又要接收連接,又要處理io讀寫,還要處理計算和業務邏輯,如果是io密集型還行,如果是計算密集型效率會很慢。現在的機器都是多核的,只用一個線程也浪費機器資源,無法充分利用機器。多線程Reactor能解決這個問題,來看一下多線程Reactor模型:

file

在多線程Reactor中,注冊接收事件都是由Reactor來做,其它的計算,編解碼由一個線程池來做。單獨一個線程接收請求,另一個線程池處理其它業務。這種模型的問題就是,使用線程池處理的時候,很多地方會有多次線程切換,上下文切換,導致效率比較低,有很多問題要處理,甚至光是接收和讀寫等請求一個線程也可能忙不過來,因此有了第三種模式,就是Multiple Reactor。

Multiple Reactor

這種模式特點是有多個Reactor,一個boss Reactor負責接收,另外幾個worker Reactor負責讀寫,把業務部分的處理還是放到線程池中來做。我們前面討論的netty示例代碼其實就是這樣的模型:

file

netty中的handler默認沒有啟用線程池,如果我們定義用線程池去處理,那么就和上面的模型幾乎一模一樣了。上面的三種Reactor模式就是最基本的Java的三種Reactor模式。

網上關於Reactor的內容有很多,分的類型也更細一些,擴展了很多其它的形式,針對具體的場景有一些更加詳細的模式,比如下面的主從Reactor模式:

file

在主從Reactor模式中,再次對Reactor進行細分,有一個主要的Reactor進行接收,里面可以做一些認證登錄之類的事情,完了之后再分配到Sub Reactor下面去,再做具體的IO處理。

網上關於Reactor模型有很多,但是Java經典的三種最基本的一定要了解清楚,我們設計自己的架構的時候,不要被網上過多的五花八門的模型擾亂,從基本模型觸發就可以。

Netty支持的Reactor形式

netty其實對三種Java基本的Reactor模式都是支持的。我們前面演示過,netty啟動時,要在ServerBootstrap中配置bossGroup和childGroup兩個EventLoopGroup,也就是說netty是可以靈活配置的。我們通過配置可以讓netty分別實現對三種模式的支持。

先看一下對單線程reactor的配置:

file

在配置的時候,boss和worker兩個group配置成一個,也就是說兩個group的工作交給一個bossGroup(線程數為1)來完成。這樣實際上就是第一個單線程reactor的模型。上面的代碼就是對第一種模式的支持。

再來看一下對第二種多線程reactor模式的支持,其實第二種模式和第一種代碼的配置是一樣的,只是在handler中進行處理的時候,使用一個線程池處理所有業務,這樣就是第二種多線程的reactor模式。

第三種Multiple Reactor模式是我們用的最多的模式,也是前面例子演示中的配置形式。boss為單線程,worker為多線程,在ServerBootstrap中同時配置boss和worker,下面是我們熟悉的代碼:

file

EventLoopGroup如果沒有配置線程數量,默認創建cpu個數的兩倍,所以上面worker的線程數根據機器決定。但是現在如果要嚴格對應第三種模式,在handler中,還要加上線程池。如果計算不太復雜,一個workerGroup就可以了,如果需要很復雜的計算,建議在handler中加入線程池進行處理。盡量不要讓IO線程池阻塞。

在很多netty項目中,還有這樣的寫法:

file

boss和worker中的線程數都是多個,這種配置並沒有對應前面提過的任何一種模式,那么會有這種需要使用多個boss線程的情況嗎?我們知道accept只能保存到一個線程上,所以說bossGroup配置多個是沒有用的,所以也有人問netty框架的作者,這種什么時候會用到,下面是作者的回答:

file

大致是說,這種配置不是必須的,但是它可能會非常有用,比如當多個ServerBootstrap共享一個NioEventLoopGroup的時候會非常有用,意思就是,每個ServerBootstrap都要注冊一個NioServerSocketChannel,如圖:

file

如果多個ServerBootstrap都使用一個bossGroup,這時候bossGroup初始化為1個線程,就意味着一個線程監聽多個端口,這樣顯然不太好,這時候bossGroup就可以配置成多個,這樣可以每個端口由一個線程去監聽,提高效率。

但是,上面是想象中的使用情況,我們實際開發netty的時候,幾乎沒有看到有多個ServerBootstrap的情況,所以bossGroup配置成多個在現在看來幾乎是沒有用的,我們把bossGroup的參數直接寫成1就可以。即使寫成了8,實際執行的時候,也只會用到一個線程,ServerBootstrap只會使用一個,不會使用多個。

關於主從reactor模式,從目前netty看沒有辦法通過配置直接實現。

本文由博客一文多發平台 OpenWrite 發布!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM