Netty源碼服務(wù)器啟動(dòng)流程

看到這篇文章的應(yīng)該都用過(guò)Netty吧乡小。Netty服務(wù)端的模板代碼如下翅溺,我們分析下它是怎么啟動(dòng)的。不要糾結(jié)沒(méi)有關(guān)閉連接的代碼诀黍,畢竟我們只是用這段代碼來(lái)debug袋坑。這篇文章我主要寫(xiě)的是Netty服務(wù)端的啟動(dòng)流程。

讀完這篇文章你會(huì)知道:

  • Netty的幾大組件的關(guān)系是什么蔗草?包括NioEventLoopGoup, NioEventLoop, Channle, ChannelHandler, Pipeline
  • Netty是怎么注冊(cè)感興趣的事件的咒彤?
  • Netty服務(wù)的bossGroup收到連接后疆柔,是怎么轉(zhuǎn)派到workerGroup的咒精。
  • Netty服務(wù)端啟動(dòng)時(shí)經(jīng)歷了什么?
package netty.simple;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class NettyServer {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 28)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new MyChatHandler());
                    }
                });
        ChannelFuture sync = bootstrap.bind(6666).sync();
        sync.channel().closeFuture().sync();
    }
}

EventLoopGroup事件循環(huán)組

Netty是基于Reactor事件模型的旷档,它將通道的連接和處理分開(kāi)模叙,bossGroup負(fù)責(zé)處理NioServerSocketChannel通道連接,通道連接后生產(chǎn)NioSocketChannel分派到workerGroup處理通道的讀寫(xiě)事件和業(yè)務(wù)邏輯鞋屈。具體怎么通道怎么分配的范咨,接下來(lái)再說(shuō)明故觅。

EventLoopGroup顧名思義,它就是EventLoop組渠啊,維護(hù)一堆EventLoop的输吏,如下圖(NioEventGroup實(shí)現(xiàn)了EventExcutor接口)。默認(rèn)new EventLoopGroup()會(huì)生成(cpu核心數(shù)*2)個(gè)EventLoop替蛉,每個(gè)EventLoop綁定一個(gè)端口贯溅,所以如果服務(wù)只綁定一個(gè)端口的話,就指定為1個(gè)接口躲查,即new EventLoopGroup(1);

quicker_2d9ab9a7-deb0-4ef4-a230-7d549ef8ae5a.png

NioEventLoop事件循環(huán)

每個(gè)EventLoop都有一個(gè)死循環(huán)它浅,負(fù)責(zé)監(jiān)聽(tīng)Channel上的事件×椭螅看NioEventLoop的繼承鏈姐霍,它是一個(gè)EventExecutor、EventLoop典唇,所以NioEventLoop是一個(gè)線程池镊折。NioEventLoop的父類SingleThreadEventExecutor維護(hù)了一個(gè)線程池對(duì)象介衔。

quicker_e527dc0a-cd93-444a-83a9-b2a57938435e.png

NioEventLoop的execute(Runable r)方法,這是線程次的提交方法与纽,當(dāng)提交一個(gè)線程到NioEventLoop中時(shí),就是執(zhí)行這個(gè)方法塘装,它做的工作如下圖,是將提交過(guò)來(lái)的線程task入隊(duì)蹦肴,然后執(zhí)行NioEventLoop的線程提交方法execute,先將task入隊(duì)阴幌,再看情況執(zhí)行startThread()勺阐。

quicker_a7b027ff-e7f7-453d-a8a2-bcc170288b05.png

跟蹤進(jìn)去矛双,它會(huì)執(zhí)行doStartThread()方法议忽。這個(gè)方法提交一個(gè)線程,立馬執(zhí)行了SingleThreadEventExecutor.this.run()方法。這里帮辟,雖然NioEventLoop沒(méi)有繼承Thread或者實(shí)現(xiàn)Runnable接口玩焰,不能認(rèn)為它是一個(gè)線程實(shí)現(xiàn)類。但是它有自己的run()方法荔棉,并且初次啟動(dòng)NioEventLoop時(shí)會(huì)執(zhí)行SingleThreadEventExecutor.this.run()方法蒿赢,因此暫且可以認(rèn)為它是一個(gè)線程吧。

quicker_db1d3441-3d5a-4923-b202-6237c5fca185.png

那NioEventLoop這個(gè)名義上的線程它的run方法是什么呢壹若,如下圖店展,他就是一個(gè)死循環(huán)了秃流。說(shuō)到這里,就知道問(wèn)什么我們說(shuō)NioEventLoop是一個(gè)死循環(huán)的線程了吧概说。

quicker_6f3f0e5b-fd81-458c-b9eb-42e4bb783504.png

Channel通道

再看到ChannelFuture f = b.bind(PORT).sync();這一句的bind方法嚣伐,它做了初始化和注冊(cè)通道的任務(wù)轩端。即ServerBootstrap初始化了注冊(cè)了通道。

quicker_95a2ed91-b18c-4556-bab1-2ef01443a210.png

他會(huì)根據(jù).channel(NioServerSocketChannel.class)這句代碼生成一個(gè)服務(wù)端的NioServerSocketChannel基茵。

quicker_425eaf88-3f90-4126-b4e2-c2b6a09cd334.png

Channel初始化時(shí)拱层,會(huì)給Channel塞很多東西,比如感興趣的OP_ACCEPT事件醋火,和非阻塞的配置箱吕,看到這里就明白了Netty底層還是NIO,Netty其實(shí)就是對(duì)NIO的封裝了兆旬。

quicker_a67418a8-0745-4e71-8504-9b2efb8f7d01.png

quicker_096e46c5-2e44-4ae8-8fde-eaa49fdcd93f.png

還向該Channel的pipeline中加入了一個(gè)Channel初始化器ChannelInitializer丽猬,那么ChannelInitializer的initChannel什么時(shí)候執(zhí)行呢熏瞄?先劇透一下脚祟,這個(gè)方法在Channel注冊(cè)完成后執(zhí)行由桌,現(xiàn)在還在Channel初始化階段邮丰,還沒(méi)執(zhí)行。

quicker_a31b15d8-0906-431d-91b4-b38e9065eaba.png

Pipeline管道

寫(xiě)過(guò)Netty小demo的都知道娃循,我們需要處理自己的業(yè)務(wù)邏輯時(shí)都是向Channel對(duì)應(yīng)的pipeline中加入我們的ChannelHandler。這里就講一下圍繞Channel的組件吧斗蒋。

如下圖捌斧,每一個(gè)Channel都有自己所屬的EventLoop和Pipeline泉沾。

quicker_073d9a21-6713-41fd-a33f-f6de019ee2c7.png

pipeline維護(hù)了鏈表形式的ChannelHandlerContext爆哑,每次addLast()添加ChannelHandler到pipeline中時(shí),都會(huì)被轉(zhuǎn)成ChannelHandlerContext并添加一個(gè)鏈表節(jié)點(diǎn)

quicker_b7df4071-be2d-4ef0-a09a-2b3faec380a2.png
quicker_598f9744-1bc2-4099-9ab0-a37ba006bf80.png

register注冊(cè)

Channel初始化完成之后,會(huì)執(zhí)行到注冊(cè)階段柱嫌,如下代碼屯换,它提交了一個(gè)任務(wù)給到EventLoop与学,上面的分析我們知道索守,提交EventLoop.execute后抑片,會(huì)先將task入隊(duì),然后啟動(dòng)死循環(huán)執(zhí)行task截汪,這里是register0方法植捎。

quicker_710c1d40-8664-4a9f-bd95-866c2d346924.png

那么register0就是異步通過(guò)EventLoop執(zhí)行的了焰枢。main線程繼續(xù)執(zhí)行,我們斷點(diǎn)在了NioEventLoop的線程枫匾,切換過(guò)去可以發(fā)現(xiàn)它啟動(dòng)了死循環(huán)拟淮。

quicker_b07b256d-a2a3-47c6-904b-1a908e0aff2b.png
quicker_10eae2ca-d9c1-42cb-95fb-cec12078cfe2.png

調(diào)到底層進(jìn)行注冊(cè)了角虫。

quicker_73d60f55-cdf6-45ec-a4d3-b9458ed64304.png

接下來(lái)看到?jīng)]有委造,它要執(zhí)行ChannelInitilizer的initChannel方法了昏兆。

quicker_bc1378d2-ea10-4911-b037-c9207bb7686d.png

它是怎么執(zhí)行的呢,如下圖隶债,調(diào)用的是pipeline的execute方法跑筝。

quicker_026b4da5-1ee8-47fc-82cd-017e846c58be.png

跟蹤進(jìn)去曲梗,開(kāi)始執(zhí)行方法了妓忍。

quicker_d57299d1-a4dc-4d53-92ff-531958ff392e.png

跳到我們最初的initChannel方法世剖⊥撸可以看到最后提交了一個(gè)task到EventLoop中引颈。往pipeline中添加了一個(gè)ServerBootstrapAcceptor蝙场。bossGroup是怎么實(shí)現(xiàn)將連接建立后分配給workGroup的呢,這就是關(guān)鍵罚拟。

quicker_b52a035d-0f7b-48e9-a55a-83868de229c4.png

SocketChannel連接分配

我們知道完箩,如果有客戶端連接到服務(wù)端的話弊知,會(huì)依次執(zhí)行pipeline中的ChannelHandler,上面我們可以看到叔扼,NioServerSocketChannle最后一個(gè)ChannelHandler就是ServerBootstrapAcceptor連接分配器漫雷。

我們用客戶端連接上來(lái),發(fā)現(xiàn)它執(zhí)行到了ServerBootstrapAcceptor的channelRead方法与柑。并拿到一個(gè)SockerChannel蓄坏,向SocketChannel添加了ChannelInitializer剑辫。然后向childGroup中注冊(cè)該SocketChannel。

quicker_b302e290-7fa2-4682-afb5-e14119f2d97e.png

ChildGroup會(huì)選擇一個(gè)EventLoop來(lái)注冊(cè)這個(gè)SocketChannel椎眯。

quicker_83e35bb4-5a5e-45ff-94ca-4b0f5ba0171e.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末编整,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子内贮,更是在濱河造成了極大的恐慌汞斧,老刑警劉巖粘勒,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異事富,居然都是意外死亡乘陪,警方通過(guò)查閱死者的電腦和手機(jī)啡邑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)募寨,“玉大人森缠,你說(shuō)我怎么就攤上這事×兄” “怎么了宾茂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵跨晴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我怀骤,道長(zhǎng),這世上最難降的妖魔是什么弓摘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任韧献,我火速辦了婚禮研叫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘果复。我一直安慰自己渤昌,他們只是感情好走搁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布私植。 她就那樣靜靜地躺著,像睡著了一般索绪。 火紅的嫁衣襯著肌膚如雪贫悄。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天唤反,我揣著相機(jī)與錄音彤侍,去河邊找鬼逆趋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛名斟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播漾橙,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼楞卡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蒋腮!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起池摧,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤作彤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后创葡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體绢慢,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胰舆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年缚窿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片够话。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡光绕,死狀恐怖诞帐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤钙态,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布册倒,位于F島的核電站磺送,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏估灿。R本人自食惡果不足惜馅袁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望犹褒。 院中可真熱鬧大溜,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至富寿,卻和暖如春锣夹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背变勇。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工搀绣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人链患。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓麻捻,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親巷折。 傳聞我的和親對(duì)象是個(gè)殘疾皇子崖咨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355