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