摘要:在上一篇源碼實(shí)戰(zhàn)系列三全剖析中,我們?cè)敿?xì)分析了的初始化過(guò)程,并得出了如下結(jié)論在中,每一個(gè)都有一個(gè)對(duì)象,并且其內(nèi)部本質(zhì)上就是一個(gè)雙向鏈表本篇我們將深入源碼內(nèi)部,對(duì)其一探究竟,給大家一個(gè)全方位解析。
在上一篇《Netty4.x 源碼實(shí)戰(zhàn)系列(三):NioServerSocketChannel全剖析》中,我們?cè)敿?xì)分析了NioServerSocketChannel的初始化過(guò)程,并得出了如下結(jié)論:
在netty中,每一個(gè)channel都有一個(gè)pipeline對(duì)象,并且其內(nèi)部本質(zhì)上就是一個(gè)雙向鏈表
本篇我們將深入Pipeline源碼內(nèi)部,對(duì)其一探究竟,給大家一個(gè)全方位解析。
Pipeline初始化過(guò)程分析在上一篇中,我們得知channel中的pipeline其實(shí)就是DefaultChannelPipeline的實(shí)例,首先我們先看看DefaultChannelPipeline的類(lèi)繼承結(jié)構(gòu)圖:
根據(jù)類(lèi)繼承結(jié)構(gòu)圖,我們看到DefaultChannelPipeline實(shí)現(xiàn)了 ChannelInboundInvoker及ChannelOutboundInvoker兩個(gè)接口。
顧名思義,一個(gè)是處理通道的inbound事件調(diào)用器,另一個(gè)是處理通道的outbound事件調(diào)用器。
inbound: 本質(zhì)上就是執(zhí)行I/O線(xiàn)程將從外部read到的數(shù)據(jù) 傳遞給 業(yè)務(wù)線(xiàn)程的一個(gè)過(guò)程。
outbound: 本質(zhì)上就是業(yè)務(wù)線(xiàn)程 將數(shù)據(jù) 傳遞給I/O線(xiàn)程, 直至發(fā)送給外部的一個(gè)過(guò)程。
如下圖所示:
我們?cè)倩氐紻efaultChannelPipeline這個(gè)類(lèi),看看其構(gòu)造方法:
protected DefaultChannelPipeline(Channel channel) { // 通道綁定channel對(duì)象 this.channel = ObjectUtil.checkNotNull(channel, "channel"); succeededFuture = new SucceededChannelFuture(channel, null); voidPromise = new VoidChannelPromise(channel, true); // 初始化頭、尾上下文 tail = new TailContext(this); head = new HeadContext(this); // 頭尾相互鏈接,形成雙向鏈表 head.next = tail; tail.prev = head; }
此構(gòu)造方法主要做了三件事:
1、綁定了當(dāng)前NioServerSocketChannel實(shí)例
2、初始化pipeline雙向鏈表的頭、尾節(jié)點(diǎn)
關(guān)于NioServerSocketChannel,我在前一篇中已經(jīng)做過(guò)詳細(xì)描述,現(xiàn)在我們著重看看head及tail這兩個(gè)屬性。
從上面的構(gòu)造方法得知,head是HeadContext的實(shí)例,tail是TailContext的實(shí)例,HeadContext與TailContext都是DefaultChannelPipeline的內(nèi)部類(lèi),它們的類(lèi)繼承結(jié)構(gòu)圖如下:
HeadContext類(lèi)繼承結(jié)構(gòu)圖
TailContext類(lèi)繼承結(jié)構(gòu)圖
從類(lèi)繼承圖我們可以看出:
1、HeadContext與TailContext都是通道的handler(中文一般叫做處理器)
2、HeadContext既可以用于outbound過(guò)程的handler,也可以用于inbound過(guò)程的handler (關(guān)于inboun和outbound上面已經(jīng)作了解釋)
3、TailContext只可以用于inbound過(guò)程的handler
4、HeadContext 與 TailContext 同時(shí)也是一個(gè)處理器上下文對(duì)象
下面我將以HeadContext為例,看看它初始化過(guò)程中到底作了哪些工作
head = new HeadContext(this);
在DefaultChannelPipeline的構(gòu)造方法中,我們看到head結(jié)點(diǎn)初始化代碼如上面所示,對(duì)應(yīng)構(gòu)造器代碼如下:
HeadContext(DefaultChannelPipeline pipeline) { super(pipeline, null, HEAD_NAME, false, true); unsafe = pipeline.channel().unsafe(); setAddComplete(); }
在其內(nèi)部,它會(huì)繼續(xù)調(diào)用父類(lèi)AbstractChannelHandlerContext的構(gòu)造器
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, boolean inbound, boolean outbound) { this.name = ObjectUtil.checkNotNull(name, "name"); this.pipeline = pipeline; this.executor = executor; this.inbound = inbound; this.outbound = outbound; ordered = executor == null || executor instanceof OrderedEventExecutor; }
此構(gòu)造方法,只是設(shè)置了當(dāng)前context對(duì)象對(duì)應(yīng)的Pipeline以及此context是作用于outbound。
AbstractChannelHandlerContext類(lèi)還有另外兩個(gè)額外屬性,他們是實(shí)現(xiàn)雙向鏈表的關(guān)鍵:
volatile AbstractChannelHandlerContext next; // 指定下一個(gè)結(jié)點(diǎn) volatile AbstractChannelHandlerContext prev; // 指定前一個(gè)結(jié)點(diǎn)
HeadContext 同時(shí)還綁定了unsafe對(duì)象,我們?cè)倩仡櫼幌聈nsafe對(duì)象。
我們從上一篇已經(jīng)得知 unsafe其實(shí)就是對(duì)java nio 通道底層調(diào)用進(jìn)行的封裝,就相當(dāng)于一個(gè)代理類(lèi)對(duì)象。
而DefaultChannelPipeline初始化時(shí),已經(jīng)綁定了channel,且由于是服務(wù)端,所以此channel是NioServerSocketChannel
protected DefaultChannelPipeline(Channel channel) { // 通道綁定channel對(duì)象 this.channel = ObjectUtil.checkNotNull(channel, "channel"); ... //非相關(guān)代碼已省略 }
所以
unsafe = pipeline.channel().unsafe();
就是
unsafe = new NioMessageUnsafe();
關(guān)于pipeline的tail結(jié)點(diǎn)初始化過(guò)程跟head差不多,這里就不作贅述了。
階段性總結(jié):Pipeline在服務(wù)端bind過(guò)程中的應(yīng)用
1、每個(gè)channel初始化時(shí),都會(huì)創(chuàng)建一個(gè)與之對(duì)應(yīng)的pipeline;
2、此pipeline內(nèi)部就是一個(gè)雙向鏈表;
3、雙向鏈表的頭結(jié)點(diǎn)是處理outbound過(guò)程的handler,尾節(jié)點(diǎn)是處理inbound過(guò)程的handler;
4、雙向鏈表的結(jié)點(diǎn)同時(shí)還是handler上下文對(duì)象;
通過(guò)《Netty4.x 源碼實(shí)戰(zhàn)系列(二):服務(wù)端bind流程詳解》 一文,我們知道,服務(wù)端channel在初始化過(guò)程中,會(huì)調(diào)用addLast方法,并傳遞了一個(gè)ChannelInitializer對(duì)象
@Override void init(Channel channel) throws Exception { // 非相關(guān)代碼已省略 ChannelPipeline p = channel.pipeline(); p.addLast(new ChannelInitializer() { @Override public void initChannel(final Channel ch) throws Exception { final ChannelPipeline pipeline = ch.pipeline(); ChannelHandler handler = config.handler(); if (handler != null) { pipeline.addLast(handler); } ch.eventLoop().execute(new Runnable() { @Override public void run() { pipeline.addLast(new ServerBootstrapAcceptor( ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)); } }); } }); }
本節(jié)我們將詳細(xì)分析一下addLast的過(guò)程 與 ChannelInitializer。
ChannelInitializer
我們先看一下ChannelInitializer的類(lèi)繼承結(jié)構(gòu)圖
通過(guò)類(lèi)繼承圖,我們得知ChannelInitializer的匿名對(duì)象其實(shí)就是一個(gè)處理inbound過(guò)程的處理器,與pipeline中的tail一樣,目前稍有不同的就是ChannelInitializer的匿名對(duì)象并不是一個(gè)context對(duì)象。
關(guān)于ChannelInitializer匿名對(duì)象的initChannel方法實(shí)現(xiàn)的內(nèi)容,本篇先不作詳述,當(dāng)講到EventLoop時(shí),我們?cè)賮?lái)回顧一下。
pipeline.addLast方法
addLast具體實(shí)現(xiàn)如下:
@Override public final ChannelPipeline addLast(ChannelHandler... handlers) { return addLast(null, handlers); }
通過(guò)此方法參數(shù),我們也可以得出,init(Channel channel)方法中的addLast其實(shí)就是想Pipeline中添加一個(gè)處理器。
addLast內(nèi)部繼續(xù)調(diào)用另一個(gè)重載方法:
@Override public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) { if (handlers == null) { throw new NullPointerException("handlers"); } for (ChannelHandler h: handlers) { if (h == null) { break; } addLast(executor, null, h); } return this; }
最終調(diào)用的是下面的重載方法(已省略非相關(guān)代碼):
@Override public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) { final AbstractChannelHandlerContext newCtx; synchronized (this) { checkMultiplicity(handler); newCtx = newContext(group, filterName(name, handler), handler); addLast0(newCtx); } return this; }
newContext方法的作用就是對(duì)傳入的handler進(jìn)行包裝,最后返回一個(gè)綁定了handler的context對(duì)象:
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) { return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler); }
新的對(duì)象是DefaultChannelHandlerContext類(lèi)的實(shí)例。
接著我們?cè)倏纯碼ddLast0方法
private void addLast0(AbstractChannelHandlerContext newCtx) { AbstractChannelHandlerContext prev = tail.prev; newCtx.prev = prev; newCtx.next = tail; prev.next = newCtx; tail.prev = newCtx; }
經(jīng)過(guò)addLast0,新包裝的context已經(jīng)添加至pipeline中了,此時(shí)的pipeline結(jié)果變化過(guò)程如下:
從addLast0代碼片段得知, 每個(gè)新添加的結(jié)點(diǎn),都是從tail結(jié)點(diǎn)之前插入
本篇總結(jié):
經(jīng)過(guò)本篇的代碼研究,對(duì)于Pipeline得出以下結(jié)論:
1、channel初始化時(shí),會(huì)同時(shí)創(chuàng)建一個(gè)與之對(duì)應(yīng)的pipeline;
2、此pipeline本質(zhì)上是一個(gè)handler處理器雙向鏈表, 用于將處理inbound及outbound過(guò)程的handler都串聯(lián)起來(lái);
3、在netty中,對(duì)于I/O處理分為兩種流向,對(duì)于獲取外部數(shù)據(jù)資源進(jìn)行處理的,都是對(duì)應(yīng)inbound,比如read等,而對(duì)于向外部發(fā)送數(shù)據(jù)資源的,都對(duì)于outbound,比如connetct及write等。
關(guān)于pipeline中的handler調(diào)用過(guò)程,后面的章節(jié)我們會(huì)做詳細(xì)分析。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/68101.html
摘要:對(duì)于,目前大家只知道是個(gè)線(xiàn)程組,其內(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í),我...
摘要:本篇將通過(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)...
摘要:而用于主線(xiàn)程池的屬性都定義在中本篇只是簡(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...
摘要:接下來(lái)的兩篇文章,我將從源碼角度為大家深入淺出的剖析的線(xiàn)程模型工作機(jī)制。我們看一下的源碼通過(guò)的代碼發(fā)現(xiàn),實(shí)現(xiàn)了接口,其內(nèi)部會(huì)通過(guò)指定的默認(rèn)線(xiàn)程工廠(chǎng)來(lái)創(chuàng)建線(xiàn)程,并執(zhí)行相應(yīng)的任務(wù)。至此,初始化完成了。下一篇我們將詳細(xì)介紹,敬請(qǐng)期待。 我們都知道Netty的線(xiàn)程模型是基于React的線(xiàn)程模型,并且我們都知道Netty是一個(gè)高性能的NIO框架,那么其線(xiàn)程模型必定是它的重要貢獻(xiàn)之一。 在使用ne...
摘要:一些想法這個(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...
閱讀 3491·2022-01-04 14:20
閱讀 3178·2021-09-22 15:08
閱讀 2273·2021-09-03 10:44
閱讀 2365·2019-08-30 15:44
閱讀 1559·2019-08-29 18:40
閱讀 2727·2019-08-29 17:09
閱讀 3057·2019-08-26 13:53
閱讀 3276·2019-08-26 13:37