Netty學(xué)習(xí)之ChannelHandler&ChannelPipeline

Netty學(xué)習(xí)之ChannelHandler&ChannelPipeline

前言

ChannelHandler

Channel生命周期

  • ChannelUnregistered,Channel被創(chuàng)建,但是還沒有注冊到EventLoop中
  • ChannelRegisted,Channel注冊到EventLoop中
  • ChannelActive,Channel激活(連接到遠(yuǎn)程端口),能夠發(fā)送以及接受消息
  • ChannelInActive,Channel沒有連接到遠(yuǎn)程端口

生命周期流程

ChannelRegisted --> ChannelActive --> ChannelInactive --> ChannelUnregistered

ChannelHandler生命周期

  • handlerAdded,當(dāng)ChannelHandler添加到ChannelPipeline時調(diào)用
  • handlerRemoved,當(dāng)ChannelHandler從ChannelPipline中移除時調(diào)用
  • exceptionCaught,在ChannelPipline處理時發(fā)生異常時調(diào)用

注意上面每個方法都接收一個ChannelHandlerContext作為參數(shù)

ChannelHandler有重要的兩個子接口

  • ChannelInboundHandler,處理輸入的數(shù)據(jù)并且處理各種狀態(tài)轉(zhuǎn)變
  • ChannelOutboundHandler,處理輸出的數(shù)據(jù)并且攔截各種操作

ChannelInboundHandler生命周期

  • channelRegistered,當(dāng)Channel注冊到對應(yīng)的EventLoop并且能夠處理I/O時調(diào)用
  • channelUnregistered,當(dāng)channel從EventLoop中移除并且不能處理任何I/O時調(diào)用
  • channelActive,當(dāng)channel激活時調(diào)用,此時channel已經(jīng)連接并且準(zhǔn)備好
  • channelInactive,當(dāng)通道斷開連接時調(diào)用
  • channelReadComplete,讀操作在Channel中已經(jīng)完成時調(diào)用(讀取完成)
  • channelRead,數(shù)據(jù)從channel中讀取時調(diào)用(開始讀取)
  • channelWritabilityChanged,寫狀態(tài)改變時調(diào)用
  • userEventTriggered,當(dāng)因為管線中傳遞對象時ChannelInboundHanlder.fireUserEventTriggered()被調(diào)用時調(diào)用

當(dāng)覆蓋ChannelInboundHandler的channelRead方法時,需要手動釋放對應(yīng)的內(nèi)存,可以通過ReferenceCountUtil.release(msg)方法進(jìn)行釋放操作

更簡單的方式是通過SimpleChannelInboundHandler,然后覆蓋其channelRead0()方法,該方法會自動釋放資源,所以,不能存儲稍后會使用到的數(shù)據(jù),因為這些數(shù)據(jù)都會失效,通過源碼就可以發(fā)現(xiàn),該方法被channelRead()調(diào)用,并且channelRead0()方法被調(diào)用后,會釋放所占用內(nèi)存。

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    boolean release = true;
    try {
        if (acceptInboundMessage(msg)) {
            @SuppressWarnings("unchecked")
            I imsg = (I) msg;
            // 這里調(diào)用channelRead0
            channelRead0(ctx, imsg);
        } else {
            release = false;
            ctx.fireChannelRead(msg);
        }
    } finally {
        if (autoRelease && release) {
            // 釋放空間
            ReferenceCountUtil.release(msg);
        }
    }
}

ChannelOutboundHandler

ChannelOutboundHandler的方法由Channel、ChannelPipeline、ChannelHandlerContext調(diào)用

主要的操作方法有write()、flush()

大部分的方法都會帶一個ChannelPromise參數(shù),當(dāng)操作完成時可以收到通知,該接口是ChannelFuture的子接口。

在Netty中,同時還提供了ChannelInboundHandlerAdapter、ChannelOutboundHanlderAdapter類,一般只需要繼承這兩個類,然后覆蓋想要實現(xiàn)邏輯的方法即可。

資源管理

在使用ChannelInboundHanlder#channelRead()或者ChannelOutboundHandler#write()時,需要保證沒有資源泄露,Netty使用引用技術(shù)來處理緩存的ByteBuf,所以使用完ByteBuf之后更改引用是非常重要的。

Netty提供了一個ResouceLeakDetecor工具類用于檢測是否發(fā)生內(nèi)存泄露,如果發(fā)生泄露,則會打印對應(yīng)的日志信息。有四個不同的級別

  • DISABLED,關(guān)閉泄露檢查
  • SIMPLE,默認(rèn)級別,樣本率為1%
  • ADVANCED,報告內(nèi)存泄露以及對應(yīng)的信息
  • PARANOID,每個類均采樣,會嚴(yán)重影響性能

如果一個消息已經(jīng)被消費或者被丟棄,并且不再在管道中傳遞給下一個接受者,則需要手動調(diào)用ReferenceCountUtil.release(msg)將其釋放,如果消息到達(dá)傳輸層,當(dāng)消息被寫出或者通道關(guān)閉后,會自動進(jìn)行釋放。

ChannelPipeline

每個新的Channel都會綁定一個新的ChannelPipeline,并且是永久性的,Channel不能綁定新的ChannelPipeline,也不能從當(dāng)前Channel中解綁(開發(fā)者無需關(guān)心綁定以及解綁)

ChannelHandlerContext使得一個ChannelHanlder能夠與ChannelPipeline以及其他的ChannelHandler進(jìn)行通信

由于ChannelPipeline傳播事件,它會檢測在管線中的下一個ChannelHandler是否匹配移動的方法,如果不匹配,則跳過該ChannleHandler,并且處理下一個,直到找到一個匹配的ChannelHandler。

ChannelPipeline中提供了眾多的方法用于操作ChannelHandler,如addFirst、addLast、remove等。同時,還提供了眾多的方法用于調(diào)用下一個ChannelInboundHandler方法的方法,如fireChannelRead()fireExceptionCaught()等,調(diào)用下一個ChannelOutboundHandler方法的方法,如flush()write()、writeAndFlush()等。

ChannelHandlerContext

ChannelHandlerContext表示ChannelHandler和ChannelPipeline之間的連接,當(dāng)一個ChannelHandler被添加到ChannelPipeline時會被創(chuàng)建。

ChannelPipeline的主要作用是管理其所關(guān)聯(lián)的ChannelHandler與其他在ChannelPipeline中的ChannelHandler的交互

如果調(diào)用Channel或者ChannelPipeline中的某些方法,對應(yīng)的操作會在整個ChannelPipeline中傳輸,而通過ChannelHandlerContext調(diào)用,只傳輸給下一個能夠處理該事件的ChannelHandler

異常處理

如果是ChannelInboundHandler中出現(xiàn)異常,會逐步進(jìn)行傳遞,所以,一般在最后一個channelHandler中覆蓋caughtException(),實現(xiàn)對應(yīng)的異常處理邏輯

如果是在ChannelOutboundHandler中出現(xiàn)異常,可以有一下機制

  • 每一個outbound操作會返回一個ChannelFuture,注冊了的ChannelFutureListener會收到對應(yīng)的消息,即成功或者失敗
  • 幾乎所有的channelOutboundHanlder會傳遞一個ChannelPromise實例,該實例是ChannelFuture的子類,ChannelPromise能夠注冊監(jiān)聽器用于異步通知,也可以直接通知操作,setSuccess()、setFailure()

總結(jié)

本小節(jié)我們主要詳細(xì)學(xué)習(xí)了ChannelHandler以及ChannelPipeline,ChannelHandler是Netty應(yīng)用中邏輯的存放地方,基本上我們主要交互的對象也是ChannelHandler,不同的ChannelHandler之間通過ChannelPipeline連接起來,構(gòu)成一個Handler處理鏈,而ChannelHandler與ChannelPipeline之間則是通過ChannelHandlerContext進(jìn)行連接,不同的對象用途不同,使用的時候需要注意。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • #易效能時間管理100天踐行#20170101(11/100) #易效能時間管理100講分享#11講 什么叫"專注...
    時間的天使閱讀 820評論 0 0
  • 身體是革命的本錢,是一切的本源,是一切美好生活的開始,如何才能擁有良好的身體,應(yīng)該從合理均衡的飲食,和堅持運動的習(xí)...
    遇見靖雯閱讀 712評論 3 11
  • 【每日一思】你一生的計劃是什么? 職業(yè),旅行,寫作 總沒有完美的計劃,現(xiàn)實常常與計劃不同,隨時隨地,一個人一句話。...
    柚子粒閱讀 259評論 0 0

友情鏈接更多精彩內(nèi)容