您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“什么是Netty事件傳播”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“什么是Netty事件傳播”吧!
Netty高性能的背后,蘊(yùn)含著優(yōu)秀的設(shè)計(jì)。 今天我們從源碼角度詳細(xì)介紹Netty中ChannelPipeline的事件,以及這些事件是如何在ChannelPipeline中傳播的。
ChannelPipeline是負(fù)責(zé)管理ChannelHandler的有序容器。 其內(nèi)部維護(hù)著一個(gè)由ChannelHandlerContext做為節(jié)點(diǎn)的雙向鏈表。 Netty上的事件便是通過(guò)這個(gè)鏈表進(jìn)行傳播的。
一圖勝千言
ChannelPipeline需要注意以下幾個(gè)關(guān)鍵點(diǎn):
Netty事件分為入站事件(inbound_event)和出站事件(outbound_event),入站事件由 InboundHandler 處理,出站事件由 OutboundHandler 處理。
事件通過(guò)ChannelHandlerContext進(jìn)行傳播,入站事件順序傳播,出站事件逆序傳播。
事件是顯示觸發(fā)的。(handler處理事件后,若要傳遞給下一個(gè)handler,必須顯示調(diào)用ChannelHandlerContext里的方法)
其中, 入站事件包括:
firefireChannelRegistered
fireChannelUnregistered
fireChannelActive
fireChannelInactive
fireChannelRead
fireChannelReadComplete
fireUserEventTriggered
fireChannelWritabilityChanged
fireExceptionCaught
出站事件包括:
bind
connect
disconnect
close
deregister
read
write
flush
writeAndFlush
handler里可以觸發(fā)任意的事件,即在InboundHandler里可以觸發(fā)出站事件,而OutboundHandler里也可以觸發(fā)入站事件。但注意不要出現(xiàn)事件的死循環(huán)。
更進(jìn)一步的,我們從源碼上去解讀ChannelPipeline的幾個(gè)要點(diǎn):
ChannelPipeline實(shí)例化后便提供了一個(gè)頭節(jié)點(diǎn)和一個(gè)尾節(jié)點(diǎn)的雙向鏈表。
protected DefaultChannelPipeline(Channel channel) { this.channel = ObjectUtil.checkNotNull(channel, "channel"); succeededFuture = new SucceededChannelFuture(channel, null); voidPromise = new VoidChannelPromise(channel, true); tail = new TailContext(this); //尾節(jié)點(diǎn) head = new HeadContext(this); //頭節(jié)點(diǎn) // 通過(guò)前驅(qū)與后續(xù)引用,形成一個(gè)雙向鏈表 head.next = tail; tail.prev = head; }
頭節(jié)點(diǎn)被標(biāo)記為outbound和inboud,尾節(jié)點(diǎn)標(biāo)記為inboud。(在下面的ChannelHandlerContext會(huì)介紹其作用)
頭節(jié)點(diǎn)的入站處理程序會(huì)觸發(fā)新的入站事件,出站處理程序會(huì)調(diào)用unsafe對(duì)象的操作(將數(shù)據(jù)輸出到Sokect)。
尾節(jié)點(diǎn)的入站處理程序是空處理或回收資源操作,出站處理程序觸發(fā)新的出站事件。
HeadContext(DefaultChannelPipeline pipeline) { super(pipeline, null, HEAD_NAME, true, true); // outbound=true unsafe = pipeline.channel().unsafe(); // 出站事件傳播到了頭節(jié)點(diǎn)后,便調(diào)用該unsafe對(duì)象上的方法 setAddComplete(); } TailContext(DefaultChannelPipeline pipeline) { super(pipeline, null, TAIL_NAME, true, false); // inboud=true setAddComplete(); } protected void onUnhandledInboundMessage(Object msg) { try { logger.debug( "Discarded inbound message {} that reached at the tail of the pipeline. " + "Please check your pipeline configuration.", msg); } finally { ReferenceCountUtil.release(msg); // 釋放引用,當(dāng)引用為0時(shí),便可回收內(nèi)存空間 } } protected void onUnhandledInboundChannelReadComplete() { // 空處理 } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { unsafe.write(msg, promise); // 通過(guò)unsafe將數(shù)據(jù)輸出 }
在Channel和ChannelPineline上的事件會(huì)傳遞給頭節(jié)點(diǎn)(入站)或尾節(jié)點(diǎn)(出站)。(再配合其他節(jié)點(diǎn)的handler,就可以形成了從頭到尾(或從尾到頭)依次傳播的事件)
@Override public final ChannelPipeline fireChannelActive() { AbstractChannelHandlerContext.invokeChannelActive(head); //觸發(fā)head節(jié)點(diǎn)入站事件 return this; } @Override public final ChannelFuture writeAndFlush(Object msg) { return tail.writeAndFlush(msg); // 觸發(fā)tail節(jié)點(diǎn)出站事件 }
addXXX(ChannelHandler)會(huì)實(shí)例化一個(gè)含handler的ChannelHandlerContext對(duì)象,并插入鏈表。
addFirst在頭節(jié)點(diǎn)后插入新節(jié)點(diǎn),addLass在尾節(jié)點(diǎn)前插入新節(jié)點(diǎn)。
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) { return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler); // 實(shí)例化含handler的ChannelHandlerContext對(duì)象 } private void addFirst0(AbstractChannelHandlerContext newCtx) { AbstractChannelHandlerContext nextCtx = head.next; newCtx.prev = head; newCtx.next = nextCtx; head.next = newCtx; // 新節(jié)點(diǎn)插入頭結(jié)點(diǎn)后面 nextCtx.prev = newCtx; } private void addLast0(AbstractChannelHandlerContext newCtx) { AbstractChannelHandlerContext prev = tail.prev; newCtx.prev = prev; newCtx.next = tail; prev.next = newCtx; tail.prev = newCtx; // 新節(jié)點(diǎn)插入尾節(jié)點(diǎn)前面 }
ChannelHandlerContext是ChannelPipeline雙向鏈表中的節(jié)點(diǎn)。 它是連接ChannelHandler和ChannelPipeline的橋梁。有了它ChannelHandler才有機(jī)會(huì)響應(yīng)并處理ChannelPipeline中的事件。
其源碼不復(fù)雜,其中ChannelHandlerContext的findContextInbound/findContextOutbound 是實(shí)現(xiàn)入站事件傳遞給InboundHandler,出站事件傳遞給OutboundHandler的關(guān)鍵。 其實(shí)現(xiàn)也比較簡(jiǎn)單,就是判斷context的后續(xù)(入站事件)/前驅(qū)(出站事件)節(jié)點(diǎn)對(duì)應(yīng)的handler實(shí)現(xiàn)是否是inbound/outbound,如果不是則遍歷下一個(gè)節(jié)點(diǎn),直至尾節(jié)點(diǎn)/頭節(jié)點(diǎn)。
而在上面的分析中,我們已經(jīng)知道尾節(jié)點(diǎn)實(shí)例化時(shí),inbound被設(shè)置為ture,頭節(jié)點(diǎn)實(shí)例化時(shí),outbound被設(shè)置為ture。 這便形成的遍歷的終止條件。
private AbstractChannelHandlerContext findContextInbound() { AbstractChannelHandlerContext ctx = this; do { ctx = ctx.next; } while (!ctx.inbound); //直到尾節(jié)點(diǎn),因?yàn)槲补?jié)點(diǎn)inbound在Pipeline中被定義為Ture return ctx; } private AbstractChannelHandlerContext findContextOutbound() { AbstractChannelHandlerContext ctx = this; do { ctx = ctx.prev; } while (!ctx.outbound);//直到頭節(jié)點(diǎn),因?yàn)轭^節(jié)點(diǎn)outbound在Pipeline中被定義為Ture return ctx; }
正是由于Netty這些優(yōu)秀的設(shè)計(jì),才使得其高性能下,仍然被靈活使用。 因此也被大家所接受并深愛著。
到此,相信大家對(duì)“什么是Netty事件傳播”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。