Reactor模式
Reactor是1995年由道格拉斯提出的一種高性能網(wǎng)絡(luò)編程模式但壮。由于好多年了粪薛,當(dāng)時的一些概念與現(xiàn)在略有不同著蟹,reactor模式在網(wǎng)絡(luò)編程中是非常重要的煞肾,可以說是NIO框架的典型模式命贴,一些經(jīng)典的框架道宅,比如Mina、Netty胸蛛、Cindy都是此模式的實現(xiàn)污茵。
我們來看看當(dāng)年提出的通用模型:
上面的圖形中:
1、Handle 可以理解為資源或者文件句柄葬项,放在netty里面就是channel泞当,就是我們實際要處理的東西
2、Event Handler和Concrete Event Handler 就是具體的事件處理器民珍,對應(yīng)netty中的handler接口和具體的handler
3襟士、Synchronous Event Demultiplexer同步事件多路復(fù)用分發(fā)器,可以理解為nio中的select
4嚷量、Initiation Dispatcher陋桂,分發(fā)器,可以理解為nio中的循環(huán)蝶溶,也就是netty中的EventLoop嗜历,處理各種事件
5、select(handlers)就是真正處理業(yè)務(wù)的地方
大家注意上面圖形中的幾個箭頭抖所,可以看出各個組件之間的關(guān)聯(lián)關(guān)系梨州。這個經(jīng)典的模型當(dāng)時并不是針對Java提出的,任何擁有這些組件的語言部蛇,都可以實現(xiàn)高性能的reactor模型摊唇。
單線程Reactor
基于Java,Doug Lea(Java并發(fā)包作者)提出了三種形式涯鲁,單線程Reactor巷查,多線程Reactor和Multiple Reactor。首先看一下基本的單線程模式:
單線程模型就是一個reactor(select+循環(huán))抹腿,客戶端(client)注冊進(jìn)來由reactor接收注冊事件岛请,然后再由reactor分發(fā)(dispatch)出去,由下面的處理器(read警绩、decode崇败、compute等)去處理。我們前面復(fù)習(xí)nio代碼的時候,程序就是這樣的結(jié)構(gòu)后室,只有一個select缩膝。
多線程Reactor
單線程的Reactor有明顯的不足,只有一個線程岸霹,又要接收連接疾层,又要處理io讀寫,還要處理計算和業(yè)務(wù)邏輯贡避,如果是io密集型還行痛黎,如果是計算密集型效率會很慢。現(xiàn)在的機(jī)器都是多核的刮吧,只用一個線程也浪費機(jī)器資源湖饱,無法充分利用機(jī)器。多線程Reactor能解決這個問題杀捻,來看一下多線程Reactor模型:
在多線程Reactor中井厌,注冊接收事件都是由Reactor來做,其它的計算致讥,編解碼由一個線程池來做旗笔。單獨一個線程接收請求,另一個線程池處理其它業(yè)務(wù)拄踪。這種模型的問題就是蝇恶,使用線程池處理的時候,很多地方會有多次線程切換惶桐,上下文切換撮弧,導(dǎo)致效率比較低,有很多問題要處理姚糊,甚至光是接收和讀寫等請求一個線程也可能忙不過來贿衍,因此有了第三種模式,就是Multiple Reactor救恨。
Multiple Reactor
這種模式特點是有多個Reactor贸辈,一個boss Reactor負(fù)責(zé)接收,另外幾個worker? Reactor負(fù)責(zé)讀寫肠槽,把業(yè)務(wù)部分的處理還是放到線程池中來做擎淤。我們前面討論的netty示例代碼其實就是這樣的模型:
netty中的handler默認(rèn)沒有啟用線程池,如果我們定義用線程池去處理秸仙,那么就和上面的模型幾乎一模一樣了嘴拢。上面的三種Reactor模式就是最基本的Java的三種Reactor模式。
網(wǎng)上關(guān)于Reactor的內(nèi)容有很多寂纪,分的類型也更細(xì)一些席吴,擴(kuò)展了很多其它的形式赌结,針對具體的場景有一些更加詳細(xì)的模式,比如下面的主從Reactor模式:
在主從Reactor模式中孝冒,再次對Reactor進(jìn)行細(xì)分柬姚,有一個主要的Reactor進(jìn)行接收,里面可以做一些認(rèn)證登錄之類的事情庄涡,完了之后再分配到Sub Reactor下面去伤靠,再做具體的IO處理。
網(wǎng)上關(guān)于Reactor模型有很多啼染,但是Java經(jīng)典的三種最基本的一定要了解清楚,我們設(shè)計自己的架構(gòu)的時候焕梅,不要被網(wǎng)上過多的五花八門的模型擾亂迹鹅,從基本模型觸發(fā)就可以。
Netty支持的Reactor形式
netty其實對三種Java基本的Reactor模式都是支持的贞言。我們前面演示過斜棚,netty啟動時,要在ServerBootstrap中配置bossGroup和childGroup兩個EventLoopGroup该窗,也就是說netty是可以靈活配置的弟蚀。我們通過配置可以讓netty分別實現(xiàn)對三種模式的支持。
先看一下對單線程reactor的配置:
在配置的時候酗失,boss和worker兩個group配置成一個义钉,也就是說兩個group的工作交給一個bossGroup(線程數(shù)為1)來完成。這樣實際上就是第一個單線程reactor的模型规肴。上面的代碼就是對第一種模式的支持捶闸。
再來看一下對第二種多線程reactor模式的支持,其實第二種模式和第一種代碼的配置是一樣的拖刃,只是在handler中進(jìn)行處理的時候删壮,使用一個線程池處理所有業(yè)務(wù),這樣就是第二種多線程的reactor模式兑牡。
第三種Multiple Reactor模式是我們用的最多的模式央碟,也是前面例子演示中的配置形式。boss為單線程均函,worker為多線程亿虽,在ServerBootstrap中同時配置boss和worker,下面是我們熟悉的代碼:
EventLoopGroup如果沒有配置線程數(shù)量苞也,默認(rèn)創(chuàng)建cpu個數(shù)的兩倍经柴,所以上面worker的線程數(shù)根據(jù)機(jī)器決定。但是現(xiàn)在如果要嚴(yán)格對應(yīng)第三種模式墩朦,在handler中坯认,還要加上線程池。如果計算不太復(fù)雜,一個workerGroup就可以了牛哺,如果需要很復(fù)雜的計算陋气,建議在handler中加入線程池進(jìn)行處理。盡量不要讓IO線程池阻塞引润。?
在很多netty項目中巩趁,還有這樣的寫法:
boss和worker中的線程數(shù)都是多個,這種配置并沒有對應(yīng)前面提過的任何一種模式淳附,那么會有這種需要使用多個boss線程的情況嗎议慰?我們知道accept只能保存到一個線程上,所以說bossGroup配置多個是沒有用的奴曙,所以也有人問netty框架的作者别凹,這種什么時候會用到,下面是作者的回答:
大致是說洽糟,這種配置不是必須的炉菲,但是它可能會非常有用,比如當(dāng)多個ServerBootstrap共享一個NioEventLoopGroup的時候會非常有用坤溃,意思就是拍霜,每個ServerBootstrap都要注冊一個NioServerSocketChannel,如圖:
如果多個ServerBootstrap都使用一個bossGroup薪介,這時候bossGroup初始化為1個線程祠饺,就意味著一個線程監(jiān)聽多個端口,這樣顯然不太好汁政,這時候bossGroup就可以配置成多個吠裆,這樣可以每個端口由一個線程去監(jiān)聽,提高效率烂完。
但是试疙,上面是想象中的使用情況,我們實際開發(fā)netty的時候抠蚣,幾乎沒有看到有多個ServerBootstrap的情況祝旷,所以bossGroup配置成多個在現(xiàn)在看來幾乎是沒有用的,我們把bossGroup的參數(shù)直接寫成1就可以嘶窄。即使寫成了8怀跛,實際執(zhí)行的時候,也只會用到一個線程柄冲,ServerBootstrap只會使用一個吻谋,不會使用多個。
關(guān)于主從reactor模式现横,從目前netty看沒有辦法通過配置直接實現(xiàn)漓拾。