Netty4源碼深入學習1-服務(wù)啟動

1.源碼準備

試用版本為


引用包版本

Netty 服務(wù)端創(chuàng)建的時序圖,如下:


服務(wù)端創(chuàng)建的時序圖

2.服務(wù)端啟動代碼說明:

舉例:服務(wù)端啟動代碼:


服務(wù)端啟動代碼

開啟一個服務(wù)端颜说,端口綁定在8888,使用nio模式,下面講下每一個步驟的處理細節(jié)

1)EventLoopGroup
 是一個死循環(huán)躲撰,不停地檢測IO事件,處理IO事件钓觉,執(zhí)行任務(wù)茴肥,后面詳細描述;
 初始化用于Acceptor的主"線程池"以及用于I/O工作的從"線程池"荡灾;
2)ServerBootstrap
初始化ServerBootstrap實例瓤狐, 此實例是netty服務(wù)端應(yīng)用開發(fā)的入口是服務(wù)端的一個啟動輔助類;
通過給它設(shè)置一系列參數(shù)來綁定端口啟動服務(wù);
3).channel(NioServerSocketChannel.class)
指定通道channel的類型批幌,由于是服務(wù)端础锐,故而是NioServerSocketChannel;
表示服務(wù)端啟動的是nio相關(guān)的channel荧缘;
channel在netty里面是一大核心概念皆警,可以理解為一條channel就是一個連接或者一個服務(wù)端bind動作;
4)b.childHandler(new NettyServerFilter())
 表示一條新的連接進來之后,該怎么處理截粗,

NettyServerFilter代碼如圖中所示:


NettyServerFilter代碼
5)ChannelFuturef =b.bind(port).sync()
這里就是真正的啟動過程了信姓,綁定6789端口,等待服務(wù)器啟動完畢绸罗,才會進入下行代碼意推。

3. 詳細描述

跳入bind()方法:
bind()方法
通過端口號創(chuàng)建一個 InetSocketAddress,然后繼續(xù)step珊蟀,進入下一步:
image.png
其中validate()方法用于驗證服務(wù)啟動需要的必要參數(shù)是否合格
ivalidate()
跳入上一層校驗菊值,校驗內(nèi)容如下,group及channelFactory是否為空育灸;
image.png
完成校驗以后進行doBind()方法:
doBind()
dobind()內(nèi)部實現(xiàn)腻窒,主要有兩個核心內(nèi)容。兩大核心一個是 initAndRegister()磅崭,以及doBind0();
a)首先看initAndRegister() 方法:
initAndRegister() 方法
核心代碼如圖中箭頭部分儿子;
1. new 一個channel,
2. init這個channel砸喻,即調(diào)用init(channel)初始化通道信息
3. 將這個channel register到某個對象柔逼。
<1> new 一個channel,step進入
channel = channelFactory().newChannel();此處相比以前版本去掉final并放入try-catch中
調(diào)用channelFactory生成通道channel實例恩够,
NioServerSocketChannel 作為clazz卒落,是通過serverbootstrap的channel方法來指定通道類型羡铲。
進一步查看channleFactory的初始化蜂桶,

此時回到NettyServer, Netty實現(xiàn)初始化AbstractBootstrap的位置也切,此時扑媚,在代碼中存在

 b.channel(NioServerSocketChannel.class); // 設(shè)置nio類型的channel

查找此方法腰湾,發(fā)現(xiàn)在AbstractBootstrap類中存在channel方法,在方法channel中初始化new一個factory:

channelFactory初始化

ReflectiveChannelFactory繼承了ChannelFactory工廠方法newChannel
所以疆股,initAndRegister中的channelFactory.newChannel()方法就是生成了一個NioServerSocketChannel的實例费坊。
image.png
  clazz.newInstance() 是通過反射的方式來創(chuàng)建一個對象,
 而這個clazz就是我們在ServerBootstrap中傳入的NioServerSocketChannel.class

進一步step旬痹,會初始化一系列變量附井,最終調(diào)用反射的clazz類,即NioServerSocketChannel两残,進行初始化永毅,


NioServerSocketChannel初始化
  private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
  //DEFAULT_SELECTOR_PROVIDER 為一個selector,

逐漸step人弓,會跳入到NioServerSocketChannel構(gòu)造方法中沼死,


NioServerSocketChannel構(gòu)造方法
[NIO SelectionKey中定義的4種事件]
  *   SelectionKey.OP_ACCEPT —— 接收連接繼續(xù)事件,**表示服務(wù)器監(jiān)聽到了客戶連接崔赌,服務(wù)器可以接收這個連接了**

  *   SelectionKey.OP_CONNECT —— 連接就緒事件意蛀,**表示客戶與服務(wù)器的連接已經(jīng)建立成功**

  *   SelectionKey.OP_READ —— 讀**就緒**事件,**表示通道中已經(jīng)有了可讀的數(shù)據(jù)健芭,可以執(zhí)行讀操作了(通道目前有數(shù)據(jù)县钥,可以進行讀操作了)**

  *   SelectionKey.OP_WRITE —— 寫**就緒**事件,**表示已經(jīng)可以向通道寫數(shù)據(jù)了(通道目前可以用于寫操作)**

   這里 注意吟榴,下面兩種魁蒜,SelectionKey.OP_READ ,SelectionKey.OP_WRITE 吩翻,

  1.當向通道中注冊SelectionKey.OP_READ事件后兜看,如果客戶端有向緩存中write數(shù)據(jù),下次輪詢時狭瞎,則會 isReadable()=true细移;

  2.當向通道中注冊SelectionKey.OP_WRITE事件后,這時你會發(fā)現(xiàn)當前輪詢線程中isWritable()一直為ture熊锭,如果不設(shè)置為其他事件

進一步往構(gòu)造方法super上層查看弧轧,最后跳入:


image.png
  將前面 provider.openServerSocketChannel(); 創(chuàng)建出來的 ServerSocketChannel保存到成員變量ch
  然后調(diào)用ch.configureBlocking(false);設(shè)置該channel為非阻塞模式
  這里的 readInterestOp 即前面層層傳入的 SelectionKey.OP_ACCEPT,

接下來重點分析 super(parent);(這里的parent其實是null碗殷,由前面寫死傳入)精绎;
在AbstractNioChannel中做了下面幾件事:

  1、繼續(xù)調(diào)用父類AbstractChannel(Channel parent)構(gòu)造方法锌妻;
  此構(gòu)造方法中代乃,主要做了三件事:
  1)、給channel生成一個新的id
  2)、通過newUnsafe初始化channel的unsafe屬性
  3)搁吓、pipeline =new DefaultChannelPipeline(this) 初始化channel的pipeline屬性
image.png
  2)在AbstractChannel類中原茅,newUnsafe()是一個抽象方法
image.png

最終實現(xiàn)來自于AbstractNioMessageChannel類中有newUnsafe()的實現(xiàn)


image.png
       此方法返回一個NioMessageUnsafe實例對象,
       而NioMessageUnsafe是AbstractNioMessageChannel的內(nèi)部類
       NioMessageUnsafe 只覆蓋了 父類AbstractNioUnsafe中的read方法堕仔,如下圖
       通過NioMessageUnsafe 及其父類的代碼便可以知道擂橘,
       其實unsafe對象是真正的負責底層channel的連接/讀/寫等操作的,
       unsafe就好比一個底層channel操作的代理對象
NioMessageUnsafe
  OP_ACCEPT都已經(jīng)注冊上了摩骨,當接收到新用戶連接時就會觸發(fā)unsafe.read()方法通贞。
 read()會不斷調(diào)用doReadMessages(),將產(chǎn)生的readBuf逐一發(fā)送給Pipeline.fireChannelRead()去處理恼五。

    private final class NioMessageUnsafe extends AbstractNioUnsafe {

        private final List<Object> readBuf = new ArrayList<Object>();

        @Override
        public void read() {
            assert eventLoop().inEventLoop();
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
            allocHandle.reset(config);

            boolean closed = false;
            Throwable exception = null;
            try {
                try {
                    do {
                        int localRead = doReadMessages(readBuf);
                        if (localRead == 0) {
                            break;
                        }
                        if (localRead < 0) {
                            closed = true;
                            break;
                        }

                        allocHandle.incMessagesRead(localRead);
                    } while (allocHandle.continueReading());
                } catch (Throwable t) {
                    exception = t;
                }

                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                readBuf.clear();
                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();

                if (exception != null) {
                    closed = closeOnReadError(exception);

                    pipeline.fireExceptionCaught(exception);
                }

                if (closed) {
                    inputShutdown = true;
                    if (isOpen()) {
                        close(voidPromise());
                    }
                }
            } finally {
                // Check if there is a readPending which was not processed yet.
                // This could be for two reasons:
                // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
                // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
                //
                // See https://github.com/netty/netty/issues/2254
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }
    }

unsafe內(nèi)容后續(xù)學習滑频;

      3)  pipeline =new DefaultChannelPipeline(this);
      # step之后發(fā)現(xiàn),最后實現(xiàn)如下圖代碼所示:
      # 初始化了HeadContext及TailContext對象唤冈。
      # head及tail初始化完成后峡迷,它們會相互連接。
      # 通過上面的代碼可以得出你虹,pipeline就是一個雙向鏈表绘搞。
DefaultChannelPipeline構(gòu)造函數(shù)
<1>部分總結(jié):
用戶調(diào)用方法 Bootstrap.bind(port) 第一步就是通過反射的方式new一個NioServerSocketChannel對象,
并且在new的過程中創(chuàng)建了一系列的核心組件傅物,進一步研究:

1夯辖、NioServerSocketChannel對象內(nèi)部綁定了Java NIO創(chuàng)建的ServerSocketChannel對象;

2董饰、Netty中蒿褂,每個channel都有一個unsafe對象,此對象封裝了Java NIO底層channel的操作細節(jié)卒暂;

3啄栓、Netty中,每個channel都有一個pipeline對象也祠,此對象就是一個雙向鏈表昙楚;
NioServerSocketChannel的類繼承結(jié)構(gòu)圖:
NioServerSocketChannel的類繼承結(jié)構(gòu)圖
<2> init這個channel
image.png
上面代碼前幾行主要進行配置,
重要內(nèi)容為最后一行
獲取當前通道的pipeline诈嘿,然后為 NioServerSocketChanne l綁定的 pipeline 添加 Handler堪旧;
此pipeline即為上面<2>中生成的pipeline
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市奖亚,隨后出現(xiàn)的幾起案子淳梦,更是在濱河造成了極大的恐慌,老刑警劉巖昔字,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爆袍,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機螃宙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來所坯,“玉大人谆扎,你說我怎么就攤上這事∏壑” “怎么了堂湖?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長状土。 經(jīng)常有香客問我无蜂,道長,這世上最難降的妖魔是什么蒙谓? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任斥季,我火速辦了婚禮,結(jié)果婚禮上累驮,老公的妹妹穿的比我還像新娘酣倾。我一直安慰自己,他們只是感情好谤专,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布躁锡。 她就那樣靜靜地躺著,像睡著了一般置侍。 火紅的嫁衣襯著肌膚如雪映之。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天蜡坊,我揣著相機與錄音杠输,去河邊找鬼。 笑死秕衙,一個胖子當著我的面吹牛抬伺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播灾梦,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼峡钓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了若河?” 一聲冷哼從身側(cè)響起能岩,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎萧福,沒想到半個月后拉鹃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年膏燕,在試婚紗的時候發(fā)現(xiàn)自己被綠了钥屈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡坝辫,死狀恐怖篷就,靈堂內(nèi)的尸體忽然破棺而出裁替,到底是詐尸還是另有隱情棋恼,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布国葬,位于F島的核電站及舍,受9級特大地震影響未辆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锯玛,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一咐柜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧攘残,春花似錦炕桨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至实撒,卻和暖如春姊途,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背知态。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工捷兰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人负敏。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓贡茅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親其做。 傳聞我的和親對象是個殘疾皇子顶考,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

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