国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

【自己讀源碼】Netty4.X系列(三) Channel Register

darkbug / 2528人閱讀

摘要:我想這很好的解釋了中,僅僅一個(gè)都這么復(fù)雜,在單線程或者說(shuō)串行的程序中,編程往往是很簡(jiǎn)單的,說(shuō)白了就是調(diào)用,調(diào)用,調(diào)用然后返回。

Netty源碼分析(三) 前提概要

這次停更很久了,原因是中途迷茫了一段時(shí)間,不過(guò)最近調(diào)整過(guò)來(lái)了。不過(guò)有點(diǎn)要說(shuō)下,前幾天和業(yè)內(nèi)某個(gè)大佬聊天,收獲很多,所以這篇博文和之前也會(huì)不太一樣,我們會(huì)先從如果是我自己去實(shí)現(xiàn)這個(gè)功能需要怎么做開(kāi)始,然后去看netty源碼,與自己的實(shí)現(xiàn)做對(duì)比。

Server端NIO復(fù)習(xí)

Netty有基于很多IO的實(shí)現(xiàn)(BIO/OIO/NIO...),而我們最常用的也就是NIO了,我們這次分析源碼,也是基于NIO的實(shí)現(xiàn),前提條件要先弄清楚NIO的流程,再分析Netty是怎么基于他開(kāi)發(fā)出這個(gè)高性能網(wǎng)絡(luò)框架的,這里我們先來(lái)簡(jiǎn)單的復(fù)習(xí)下,已經(jīng)熟悉的同學(xué)可以跳過(guò)不看了。

四個(gè)步驟

拋開(kāi)數(shù)據(jù)的讀寫(xiě),我們把NIO服務(wù)端監(jiān)聽(tīng)分成四個(gè)步驟

channel初始化

注冊(cè) selector到 channel上

channel綁定端口

循環(huán)select 等待事件

其中第二步又分為幾個(gè)小步驟

創(chuàng)建selector

調(diào)用channel的register

然后第四步也分為幾個(gè)步驟

selector.select(或者幾個(gè)帶參的重寫(xiě)方法)

處理所有的selectedKey

然后再繼續(xù)分解:[處理所有的selectedKey]

獲取selectedKey的channel

根據(jù)selectedKey的狀態(tài)處理channel

其中需要注意的是:如果key的狀態(tài)(或者說(shuō)是readyops)是accept的話,需要把channel轉(zhuǎn)成ServerSocktChannel然后通過(guò)accept獲取新的channel,再把這個(gè)selector注冊(cè)到這個(gè)channel中去。如果是其他的讀寫(xiě)狀態(tài)的話,獲取的channel要轉(zhuǎn)為SocketChannel,然后進(jìn)行操作。

Register的實(shí)現(xiàn)

通過(guò)NIO的復(fù)習(xí),我們可以看到一個(gè)完整的NIO的創(chuàng)建流程。在之前的兩篇博文中,我們實(shí)現(xiàn)了第一步初始化以及第三步綁定端口的流程,現(xiàn)在我們先來(lái)實(shí)現(xiàn)channel的register。

這里多提一句,網(wǎng)上很多人都是在bind端口之后,再channel register的,但是其實(shí)這個(gè)前后順序并沒(méi)有關(guān)系,Netty中則是在bind之前進(jìn)行的。

先看下之前的代碼,我們?cè)赿oBind方法中,先實(shí)例化了一個(gè)channel,然后調(diào)用channel的bind方法。

 private  void doBind(Integer port){
        Channel channel = channelFactory.newChannel();
        channel.bind(new InetSocketAddress(port));
}

這樣看,我們現(xiàn)在需要做的就是在這兩行代碼中插入一段channel register的代碼就可以了,那是不是可以這樣,Channel中新增一個(gè)register接口,然后在實(shí)現(xiàn)類(lèi)中去實(shí)現(xiàn)他,最后在這里中間寫(xiě)一句channel.register,貌似看起來(lái)沒(méi)問(wèn)題啊,不管怎么樣,先動(dòng)手實(shí)現(xiàn)下。

/* in Channel Interface */
void register(Selector selector);

/* in AbstractNioChannel */
@Override
public void register(Selector selector)  throws ClosedChannelException {
    ch().register(selector,0,null);
}
/*in AbstractBootstrap*/
try {
    channel.register(Selector.open());
} catch (IOException e) {
    e.printStackTrace();
}

我們最后的doBind看起來(lái)就會(huì)是這樣

 private  void doBind(Integer port){
        Channel channel = channelFactory.newChannel();
        try {
            channel.register(Selector.open());
        } catch (IOException e) {
            e.printStackTrace();
        }
        channel.bind(new InetSocketAddress(port));
    }

功能貌似是實(shí)現(xiàn)了。。。但是,看起來(lái)是不是有些問(wèn)題?

沒(méi)錯(cuò),首先看起來(lái)很丑,try/catch一大塊的很難看,但其次拋去美觀問(wèn)題不談,這么寫(xiě)不符合設(shè)計(jì)思想,我這個(gè)類(lèi)是一個(gè)抽象的啟動(dòng)類(lèi),但是這個(gè)register只是NIO的寫(xiě)法,那BIO,AIO...其他IO實(shí)現(xiàn)怎么辦?

那我們先來(lái)做下最簡(jiǎn)單的改造,軟件不就是在一次次的重構(gòu)中,慢慢成長(zhǎng)起來(lái)的嘛。我簡(jiǎn)單的分了下步驟。

Channel接口更改,取消入?yún)⒑彤惓伋?/p>

    void register();

抽象實(shí)現(xiàn)類(lèi)中增加新的帶參方法,供接口方法調(diào)用

@Override
public void register(){
    try {
            register(Selector.open());
    } catch (IOException e) {
            e.printStackTrace();
    }
}

private void register(Selector selector) throws ClosedChannelException {
    ch().register(selector,0,null);
}

doBind內(nèi)register調(diào)用

private  void doBind(Integer port){
    Channel channel = channelFactory.newChannel();
    channel.register();
    channel.bind(new InetSocketAddress(port));
}

這樣看,代碼是不是美觀很多?最關(guān)鍵的是,此時(shí)的我們的抽象啟動(dòng)類(lèi)里,真正做到了抽象,不管什么IO實(shí)現(xiàn),都會(huì)去調(diào)用自身Channel實(shí)現(xiàn)的register方法。

目前來(lái)看還挺不錯(cuò)的,接下來(lái)看下Netty是怎么實(shí)現(xiàn)的吧,自己寫(xiě)的思路對(duì)的,但是肯定有很多地方?jīng)]考慮到的。

Netty中的Register How to do

Netty中,doBind方法里是包裝了一個(gè)initAndRegister方法去完成初始化和注冊(cè)地功能,這里我們直接看下initAndRegister這個(gè)方法體,看下Netty怎么做的

是不是和我們寫(xiě)的差不多呢,先實(shí)例化一個(gè)channel,然后再進(jìn)行register的,不過(guò)有兩點(diǎn)不同的地方,其是register之前先要調(diào)用一個(gè)init方法,玩過(guò)Netty的應(yīng)該都清楚,主要是處理我們自定義的一些配置的,這里我們先不提,等后面再說(shuō)。先來(lái)說(shuō)下第二個(gè)不同,也就是register方法。

我們的代碼里,是直接通過(guò)擴(kuò)展channel的接口,直接調(diào)用channel的register的方法的,但是Netty這里則是通過(guò)group(包含了Netty很重要的一個(gè)角色EventLoop,我們后面會(huì)詳細(xì)說(shuō)他),傳入channel對(duì)象,然后再去調(diào)用channel的register方法,不信,我們看下他的調(diào)用鏈。

撇去Netty復(fù)雜的繼承關(guān)系,我們最終定位到方法的最終調(diào)用的地方,SingleThreadEventLoop里

我們可以看到,register方法里,先是包裝了一個(gè)promise對(duì)象(實(shí)現(xiàn)promise接口的對(duì)象,這里維護(hù)了channel對(duì)象和這個(gè)eventloop對(duì)象,題外話:promise接口其實(shí)是future接口的一個(gè)超集),然后調(diào)用了promise里的channel的unsafe對(duì)象的register(ps:好繞口的感覺(jué)),這個(gè)unsafe就是channel的一個(gè)內(nèi)部類(lèi),感覺(jué)越來(lái)越接近了,我們就到unsafe里面看下register方法吧。

這下應(yīng)該很清晰了吧,unsafe的register里又調(diào)用了doRegister,然后就和我們的方法差不多了,這里的javaChannel其實(shí)就是JAVA NIO的channel對(duì)象,唯一不同的就是這里的selector不是open出來(lái)的,而是早在eventloop初始化的時(shí)候就存下來(lái)了,可能有人會(huì)問(wèn):這里eventloop是怎么來(lái)的呢?答案就在上面,在調(diào)用unsafe的register的時(shí)候,我們傳入了兩個(gè)對(duì)象,第一個(gè)this就是指向的eventloop(別忘了我們是在eventloop里調(diào)用的register)。然后就是把this(AbstractNioChannel)作為register的第三個(gè)參數(shù)(后面可以通過(guò)selectedKey.attachment獲取到)

How to say

Netty中怎么做的我們也看過(guò)了,相信你和我一樣,有很多疑問(wèn)(大神們請(qǐng)自動(dòng)無(wú)視)。這里我舉出我覺(jué)得比較重要的兩個(gè)。

為什么一個(gè)注冊(cè)這么復(fù)雜?明明用原生NIO只需要一行就搞定的,又是promise又是unsafe,在channel.register之間抽象出來(lái)兩層。

為什么最后的doRegister里,要用一個(gè)無(wú)限循環(huán)包起來(lái)呢?

答案還是要從源碼里獲得,先看下eventloop的register的接口注釋

簡(jiǎn)單來(lái)說(shuō),就是把eventloop對(duì)象注冊(cè)到channel里,并且要能知道注冊(cè)的結(jié)果,并且返回。這句話,可能有些同學(xué)不太明白,這是Java中的異步調(diào)用,我們傳統(tǒng)開(kāi)發(fā)的Java web程序,我們所處理的基本都是單線程的模式,首先這符合人的大腦的習(xí)慣嘛(人的大腦本質(zhì)是串行處理事情的),其次是多線程的部分,框架和容器已經(jīng)幫我做好了,所以可能會(huì)不太理解異步這個(gè)概念。

然后再看下unsafe這個(gè)register接口的注釋

看到了吧,執(zhí)行promise的channel的注冊(cè)并在完成的時(shí)候通知返回,這是靠一個(gè)叫觀察者模式來(lái)完成的,具體的內(nèi)容在下一章會(huì)詳細(xì)講解。

我想這很好的解釋了Netty中,僅僅一個(gè)register都這么復(fù)雜,在單線程或者說(shuō)串行的程序中,編程往往是很簡(jiǎn)單的,說(shuō)白了就是調(diào)用,調(diào)用,調(diào)用然后返回。但Netty是個(gè)充滿并行和異步的程序,所以光從設(shè)計(jì)上就會(huì)比較復(fù)雜,這讓我想起了沈神說(shuō)的一句話:簡(jiǎn)單的容易理解的模型性能都差,性能好的都很復(fù)雜。雖然他指的是數(shù)據(jù)庫(kù)的設(shè)計(jì),但是我覺(jué)得道理是相通的。

還有一點(diǎn)就是這些設(shè)計(jì)也是為了同時(shí)兼容服務(wù)端和客戶(hù)端,軟件開(kāi)發(fā)的思想里,很重要的一點(diǎn)就是復(fù)用。這也是為什么會(huì)有第二個(gè)疑問(wèn),我們還是要看下注釋。


jdk的register的注釋說(shuō)明,如果這個(gè)channel已經(jīng)被注冊(cè)過(guò)了,并且再次注冊(cè)的過(guò)程中連接斷了,則會(huì)拋出這個(gè)CanceledKeyException異常,那么這里捕獲了異常并且調(diào)用select.selectNow,理論上會(huì)從selector中移除這個(gè)無(wú)效的channel,但是事實(shí)上并沒(méi)有,所以下面也寫(xiě)到了,可能是個(gè)jdk bug,所以要顯示的往外繼續(xù)拋出異常。

仔細(xì)想想,如果僅僅是服務(wù)端的channel register,怎么可能發(fā)生上述的情況呢,所以這里的doRegister同時(shí)也是客戶(hù)端channel復(fù)用的注冊(cè)方法。

總結(jié)&&預(yù)告

這一章差不多就結(jié)束了,我們做了哪些事情呢?

復(fù)習(xí)Java NIO 流程

動(dòng)手實(shí)現(xiàn)channel register

讀Netty源碼,理解Netty是如何實(shí)現(xiàn)register以及為什么這樣實(shí)現(xiàn)的

軟件開(kāi)發(fā)的思想

然后畫(huà)個(gè)簡(jiǎn)單的圖來(lái)總結(jié)下把。

接下來(lái)在實(shí)現(xiàn)循環(huán)select監(jiān)聽(tīng)和處理事件之前,我們先實(shí)現(xiàn)一下Netty中一個(gè)很重要的EventLoop,并且了解下運(yùn)用到的異步框架(Future and Promise),希望我的拖延癥得到解決,應(yīng)該在下周會(huì)寫(xiě)出來(lái)。。。

希望同學(xué)們看到后會(huì)有收獲,如果我有理解不對(duì)的地方,歡迎來(lái)指正。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/68364.html

相關(guān)文章

  • Netty4.x 源碼實(shí)戰(zhàn)系列(二):服務(wù)端bind流程詳解

    摘要:對(duì)于,目前大家只知道是個(gè)線程組,其內(nèi)部到底如何實(shí)現(xiàn)的,它的作用到底是什么,大家也都不太清楚,由于篇幅原因,這里不作詳細(xì)介紹,后面會(huì)有文章作專(zhuān)門(mén)詳解。 在上一篇《ServerBootstrap 與 Bootstrap 初探》中,我們已經(jīng)初步的了解了ServerBootstrap是netty進(jìn)行服務(wù)端開(kāi)發(fā)的引導(dǎo)類(lèi)。 且在上一篇的服務(wù)端示例中,我們也看到了,在使用netty進(jìn)行網(wǎng)絡(luò)編程時(shí),我...

    laoLiueizo 評(píng)論0 收藏0
  • 自己源碼Netty4.X系列(二) 啟動(dòng)類(lèi)成員Channel

    摘要:下面無(wú)恥的貼點(diǎn)源碼。啟動(dòng)類(lèi)我們也學(xué),把啟動(dòng)類(lèi)抽象成兩層,方便以后寫(xiě)客戶(hù)端。別著急,我們慢慢來(lái),下一篇我們會(huì)了解以及他的成員,然后,完善我們的程序,增加其接收數(shù)據(jù)的能力。文章的源碼我會(huì)同步更新到我的上,歡迎大家,哈哈。 廢話兩句 這次更新拖了很長(zhǎng)時(shí)間,第一是自己生病了,第二是因?yàn)樽铋_(kāi)始這篇想寫(xiě)的很大,然后構(gòu)思了很久,發(fā)現(xiàn)不太合適把很多東西寫(xiě)在一起,所以做了點(diǎn)拆分,準(zhǔn)備國(guó)慶前完成這篇博客。...

    waterc 評(píng)論0 收藏0
  • 自己源碼Netty4.X系列(一) 啟動(dòng)類(lèi)概覽

    摘要:一些想法這個(gè)系列想開(kāi)很久了,自己使用也有一段時(shí)間了,利用也編寫(xiě)了一個(gè)簡(jiǎn)單的框架,并運(yùn)用到工作中了,感覺(jué)還不錯(cuò),趁著這段時(shí)間工作不是很忙,來(lái)分析一波源碼,提升下技術(shù)硬實(shí)力。 一些想法 這個(gè)系列想開(kāi)很久了,自己使用netty也有一段時(shí)間了,利用netty也編寫(xiě)了一個(gè)簡(jiǎn)單的框架,并運(yùn)用到工作中了,感覺(jué)還不錯(cuò),趁著這段時(shí)間工作不是很忙,來(lái)分析一波源碼,提升下技術(shù)硬實(shí)力。 結(jié)構(gòu) 這里先看下net...

    qingshanli1988 評(píng)論0 收藏0
  • Netty4.x 源碼實(shí)戰(zhàn)系列):NioServerSocketChannel全剖析

    摘要:本篇將通過(guò)實(shí)例化過(guò)程,來(lái)深入剖析。及初始化完成后,它們會(huì)相互連接。我們?cè)诨氐降臉?gòu)造方法父類(lèi)構(gòu)造方法調(diào)用完成后,還要初始化一下自己的配置對(duì)象是的內(nèi)部類(lèi)而又是繼承自,通過(guò)代碼分析,此對(duì)象就是就會(huì)對(duì)底層一些配置設(shè)置行為的封裝。 根據(jù)上一篇《Netty4.x 源碼實(shí)戰(zhàn)系列(二):服務(wù)端bind流程詳解》所述,在進(jìn)行服務(wù)端開(kāi)發(fā)時(shí),必須通過(guò)ServerBootstrap引導(dǎo)類(lèi)的channel方法來(lái)...

    Flink_China 評(píng)論0 收藏0
  • Netty4.x 源碼實(shí)戰(zhàn)系列(一):ServerBootstrap 與 Bootstrap 初探

    摘要:而用于主線程池的屬性都定義在中本篇只是簡(jiǎn)單介紹了一下引導(dǎo)類(lèi)的配置屬性,下一篇我將詳細(xì)介紹服務(wù)端引導(dǎo)類(lèi)的過(guò)程分析。 從Java1.4開(kāi)始, Java引入了non-blocking IO,簡(jiǎn)稱(chēng)NIO。NIO與傳統(tǒng)socket最大的不同就是引入了Channel和多路復(fù)用selector的概念。傳統(tǒng)的socket是基于stream的,它是單向的,有InputStream表示read和Outpu...

    BakerJ 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<