Netty5.0工作原理

前言:

前面章節(jié)我們對(duì)Netty的整體結(jié)構(gòu)和使用流程進(jìn)行了剖析遭贸,使用過(guò)程中我們首先創(chuàng)建了兩個(gè)線程組EventLoopGroup,一個(gè)負(fù)責(zé)連接分派心软,一個(gè)負(fù)責(zé)IO讀寫(xiě)壕吹,那么這兩個(gè)線程組工作原理是怎么樣的呢?由于NioEventLoopGroup應(yīng)用較為廣泛删铃,我們從這個(gè)線程組開(kāi)刀耳贬!

結(jié)論:

由于文章源碼冗長(zhǎng),如果你沒(méi)興趣看(我猜測(cè)你應(yīng)該沒(méi)興趣)猎唁,直接看結(jié)論咒劲。

一:NioEventLoopGroup構(gòu)建的兩個(gè)線程組boss和worker,線程個(gè)數(shù)默認(rèn)為CPU個(gè)數(shù)的2倍诫隅。

二:NioEventLoopGroup中的(boss和worker)線程底層通過(guò)Java NIO中的Selector實(shí)現(xiàn)對(duì)通道的綁定和監(jiān)聽(tīng)腐魂。

三:boss線程執(zhí)行通道連接,當(dāng)監(jiān)聽(tīng)到read事件之后綁定worker線程和通道逐纬。worker線程執(zhí)行通道的IO讀寫(xiě)蛔屹。

四:netty本質(zhì)上還是NIO而不是AIO,只不過(guò)執(zhí)行的線程我們可以指定豁生。

1:Reactor模型:

Reactor模型分為三種兔毒,根據(jù)并發(fā)的用戶量性能由低到高

第一種是單線程接收多客戶端連接請(qǐng)求并親自處理IO讀寫(xiě)

第二種單線程接收多客戶端連接請(qǐng)求請(qǐng)求业筏,轉(zhuǎn)發(fā)給其他線程池進(jìn)行IO讀寫(xiě)

第三種多線程接收多客戶端連接請(qǐng)求吧雹,轉(zhuǎn)發(fā)給其他線程池進(jìn)行IO讀寫(xiě)

Netty可以完全吸納該模型,server端可以通過(guò)設(shè)置bossgroup和workergroup來(lái)選擇使用第二種和第三種模型愁溜。

2:NioEventLoop線程模型:

我們用到的線程組EventLoopGroup workerGroup = new NioEventLoopGroup()實(shí)際上是有每個(gè)NioEventLoop線程模型組成的芍殖。

類似于web天生的多線程豪嗽,只要每個(gè)業(yè)務(wù)邏輯的Handler(車間)是無(wú)狀態(tài)的,那么所有的NioEventLoop(工人)就可以并發(fā)執(zhí)行豌骏,而不需要加鎖龟梦。

A:NioEventLoop原理分析:

NioEventLoop類圖

由該類圖可以看出NioEventLoop線程模型頂層接口實(shí)現(xiàn)了ScheduledExecutorService,這是Java concurrent包里面的接口肯适,該接口可以執(zhí)行定時(shí)任務(wù)变秦,所以毫無(wú)疑問(wèn),NioEventLoop模型也能執(zhí)行任務(wù)并且能執(zhí)行定時(shí)任務(wù)框舔!

打開(kāi)源碼蹦玫,我們發(fā)現(xiàn)NioEventLoop內(nèi)部依賴了一個(gè)Selector,這個(gè)Selector是Java NIO中的Selector刘绣,所以可見(jiàn)Netty本質(zhì)上并不是AIO而是NIO樱溉。

那么,NioEventLoop到底能干哪些事呢纬凤?

a:打開(kāi)了Selector福贞,run方法中,根據(jù)是否有要執(zhí)行的任務(wù)來(lái)選擇調(diào)用selectNow方法和select方法(該方法沒(méi)有會(huì)輪詢)停士,值得深思的是一個(gè)線程模型打開(kāi)一個(gè)Selector挖帘。

b:注冊(cè)通道完丽,注冊(cè)通道方法register,通過(guò)該方法綁定通道和線程拇舀。

c:執(zhí)行任務(wù)逻族,run方法最終調(diào)用的processSelectedKey方法是真正執(zhí)行任務(wù)的代碼,該方法通過(guò)Java NIO中的SelectionKey和channel真正執(zhí)行執(zhí)行通道的IO讀寫(xiě)操作骄崩。

以上過(guò)程是不是有種似曾相識(shí)的感覺(jué)聘鳞?沒(méi)錯(cuò),就是Java NIO的操作流程的封裝要拂!

3:NioEventLoopGroup原理分析:

我們前面說(shuō)了NioEventLoop能干什么抠璃,但是并沒(méi)有說(shuō)怎么干的,誰(shuí)指使他干的脱惰?我們下面回答這個(gè)問(wèn)題搏嗡。

首先,我們新建NioEventLoopGroup線程組枪芒,追蹤其源碼調(diào)用鏈

1
2
3
4
5
6

好彻况,追蹤到這里我們可以發(fā)現(xiàn),在不填寫(xiě)參數(shù)的情況下創(chuàng)建的線程數(shù)是CPU個(gè)數(shù)的2倍舅踪!繼續(xù)往下追蹤.....

7
7.1

調(diào)用鏈到這里只有nThreads是有值的纽甘,executor為空,所以新建了一個(gè)ThreadPerTaskExecutor抽碌,這個(gè)類本質(zhì)上是一個(gè)Java的Executor線程執(zhí)行器悍赢,可以執(zhí)行任務(wù),繼續(xù)货徙。左权。。痴颊。

7.2

還是7環(huán)節(jié)中的構(gòu)造方法赏迟,我們看到,這個(gè)MultiThreadEventLoopGroup內(nèi)置了一個(gè)線程執(zhí)行器數(shù)組蠢棱,數(shù)組長(zhǎng)度與線程個(gè)數(shù)相同锌杀,下面調(diào)用了newChild給每個(gè)執(zhí)行器數(shù)組元素賦值,點(diǎn)擊進(jìn)入newChild方法泻仙,由于我們調(diào)用的是NioEventLoopGroup糕再,所以進(jìn)入NioEventLoop的實(shí)現(xiàn)。玉转。突想。。

7.3
7.3.1
7.3.2

看見(jiàn)沒(méi),這個(gè)new NioEventLoop(this,executor,(SelectorProvider)args[0])猾担;方法袭灯,這就是最終NioEventLoopGroup線程組的組成元素,構(gòu)造NioEventLoop過(guò)程中給它傳入了我們的ThreadPerTaskExecutor執(zhí)行器绑嘹。最后這個(gè)調(diào)用鏈還調(diào)用了openSelector()啟動(dòng)Selector<寺!圾叼!

總結(jié):壓縮以上過(guò)程,NioEventLoopGroup通過(guò)內(nèi)置EventExecutor數(shù)組而實(shí)現(xiàn)線程組捺癞,而EventExecutor數(shù)組內(nèi)部元素是NioEventLoop夷蚊,所以可以說(shuō)NioEventLoopGroup在構(gòu)造過(guò)程中創(chuàng)建了2*CPU個(gè)NioEventLoop待用!K杞椤L韫摹!

4:?jiǎn)?dòng):

前面我們剖析了NioEventLoopGroup是怎么由NioEventLoop構(gòu)成的唐础,下面我們分析下NioEventLoop是怎么工作的箱歧!

1

我們直接看圖1,bootstrap.bind方法一膨,點(diǎn)擊進(jìn)入調(diào)用鏈

2

綁定IP和端口呀邢。。豹绪。

3
4

調(diào)用至此价淌,首先initAndRegister方法初始化了一條通道,并把ChannelFuture預(yù)期也綁定到了通道上瞒津。...

4.1

可以看到initAndRegister創(chuàng)建了一條通道并對(duì)通道進(jìn)行了初始化蝉衣,這個(gè)過(guò)程實(shí)際上把Channel通道內(nèi)部的eventLoop設(shè)置成了bossGroup中的NioEventLoop

4.1.1

這是ServerBootstrap中的方法,group()方法返回的實(shí)際上是bossGroup巷蚪,next()方法則從bossGroup中返回一個(gè)NioEventLoop病毡。

直到目前,所有的工作還都是有main線程來(lái)執(zhí)行的屁柏,channelFactory方法返回的是ServerBootstrapChannelFactory啦膜,newChannel方法創(chuàng)建的是NioServerSocketChannel(還記得你寫(xiě)的代碼里.channel(NioServerSocketChannel)方法嗎?這里的channel就是根據(jù)這個(gè)來(lái)的前联,具體源碼略)

4.1.2
4.1.3

NioServerSocketChannel實(shí)例化的時(shí)候設(shè)置了感興趣的通道事件為OP_ACCEPT9ζ荨!似嗤!沿著這個(gè)實(shí)例化鏈啸臀,一直到AbstractChannel,最終給該實(shí)例設(shè)置了pipeline、unsafe乘粒、bossGroup的NioEventLoop三個(gè)成員變量豌注。我們繼續(xù)查看4.1環(huán)節(jié)中的channel.unsafe.register()方法,我們現(xiàn)在知道channel是我們剛剛創(chuàng)建的NioServerSocketChannel實(shí)例灯萍,unsafe方法返回的是該實(shí)例的成員變量轧铁,該實(shí)例內(nèi)部還有一個(gè)bossGroup的線程模型實(shí)例。

4.1.4

這個(gè)register方法是AbstractChannel中的方法旦棉,所以eventLoop就是boss線程模型齿风,boss線程模型執(zhí)行register0方法,所以這個(gè)register方法綁定了通道與bossgroup線程0舐濉>劝摺!繼續(xù)看init(channel)方法真屯、脸候、、

4.1.5

main線程執(zhí)行的init方法是ServerBootstrap中的方法绑蔫,其中handler()方法就是我們代碼里寫(xiě)的.handler(handler)运沦。init方法內(nèi)部還創(chuàng)建了一條流水線pipeline,并在流水線里加入了一個(gè)ServerBootstrapAcceptor配深,注意携添,我們用的是netty5.0,這和netty4.0里是有變化的凉馆,netty5.0并沒(méi)有直接給ServerBootstrapAcceptor設(shè)置一個(gè)workerGroup薪寓,而僅僅是設(shè)置了一些childHandler!@焦病向叉!

我們繼續(xù)追蹤,希望找到通道是怎么與workerGroup綁定的`露D富选!京革!繼續(xù)追蹤doBind0方法.....

5
6
7
8
8.1

doBind0中我們發(fā)現(xiàn)奇唤,剛剛初始化并且綁定了NioEventLoop的方法調(diào)用了eventLoop()方法得到了一個(gè)Boss的NioEventLoop,然后調(diào)用NioEventLoop的execute方法執(zhí)行一個(gè)任務(wù)匹摇,execute方法是NioEventLoop的父類SingleThreadEventExecutor的方法咬扇,該方法內(nèi)部調(diào)用了startThread方法、doStartThread方法廊勃,最終到executor.execute方法懈贺,而這里的executor就是我們?cè)趎ew NioEventLoopGroup的內(nèi)部創(chuàng)建的那個(gè)ThreadPerTaskExecutor经窖,該方法中調(diào)用了SingleThreadEventExecutor.this.run(),而這個(gè)run方法正是我們的NioEventLoop的run方法K蟛印;隆!

我們追蹤run方法可以找到一條調(diào)用鏈processSelectedKeysPlain(selector.selectedKeys())-->processSelectedKeysPlain-->processSelectedKey-->

8.2

最終調(diào)用了8.2的方法堡妒,整個(gè)調(diào)用鏈都是由bossGroup中的線程執(zhí)行的配乱,知道這里unsafe.read()方法,該方法會(huì)繼續(xù)調(diào)用鏈皮迟,一直到

8.3

一直到doReadMessages方法搬泥,可以看到,該方法內(nèi)部new NioSocketChannel里邊設(shè)置了綁定了一個(gè)workerGroup的線程7帷S蛹亍!回到doReadMessages上一層烦粒,我們這里給綁定一個(gè)worker線程之后,boss線程就會(huì)調(diào)用fireChannelRead方法代赁,這時(shí)候真正執(zhí)行的線程就是worker了H潘!芭碍!


我們?cè)俅螌徱曄耇hreadPerTaskExecutor這個(gè)類:

9

剛剛的execute方法就是這里的execute徒役,這里先利用線程工廠新建了一個(gè)線程,然后調(diào)用了線程的start()方法窖壕,真正的啟動(dòng)了NioEventLoop這個(gè)線程的run方法S俏稹!瞻讽!

OK鸳吸,以上這個(gè)過(guò)程解釋了NioEventLoop線程模型怎么干的問(wèn)題!

總結(jié):追蹤源碼是個(gè)很令人頭疼的問(wèn)題速勇,由于都是面向接口編程晌砾,所以我們觀察的時(shí)候要十分注意不要看錯(cuò)了方法,有的方法是父類的方法烦磁,有的方法是子類本身的方法Q佟!都伪!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呕乎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子陨晶,更是在濱河造成了極大的恐慌猬仁,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異逐虚,居然都是意外死亡聋溜,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門叭爱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)撮躁,“玉大人,你說(shuō)我怎么就攤上這事买雾“崖” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵漓穿,是天一觀的道長(zhǎng)嗤军。 經(jīng)常有香客問(wèn)我,道長(zhǎng)晃危,這世上最難降的妖魔是什么叙赚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮僚饭,結(jié)果婚禮上震叮,老公的妹妹穿的比我還像新娘。我一直安慰自己鳍鸵,他們只是感情好苇瓣,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著偿乖,像睡著了一般击罪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贪薪,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天媳禁,我揣著相機(jī)與錄音,去河邊找鬼画切。 笑死损话,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的槽唾。 我是一名探鬼主播丧枪,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼庞萍!你這毒婦竟也來(lái)了拧烦?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤钝计,失蹤者是張志新(化名)和其女友劉穎恋博,沒(méi)想到半個(gè)月后齐佳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡债沮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年炼吴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疫衩。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡硅蹦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闷煤,到底是詐尸還是另有隱情童芹,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布鲤拿,位于F島的核電站假褪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏近顷。R本人自食惡果不足惜生音,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窒升。 院中可真熱鬧久锥,春花似錦、人聲如沸异剥。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)冤寿。三九已至,卻和暖如春青伤,著一層夾襖步出監(jiān)牢的瞬間督怜,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工狠角, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留号杠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓丰歌,卻偏偏與公主長(zhǎng)得像姨蟋,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子立帖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容