netty原始碼分析之-SimpleChannelInboundHandler與ChannelInboundHandlerAdapter詳解(6)
每一個Handler都一定會處理出站或者入站(也可能兩者都處理)資料,例如對於入站的Handler可能會繼承SimpleChannelInboundHandler或者ChannelInboundHandlerAdapter,而SimpleChannelInboundHandler又是繼承於ChannelInboundHandlerAdapter,最大的區別在於SimpleChannelInboundHandler會對沒有外界引用的資源進行一定的清理,並且入站的訊息可以通過泛型來規定。
對於兩者關係:
public abstract class SimpleChannelInboundHandler <I> extends ChannelInboundHandlerAdapter
對於ChannelInboundHandlerAdapter的實現,會實現ChannelInboundHandler中的所有方法:
public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler
但是我們可能只會重寫一些我們感興趣的方法來處理資料,這裡使用的是介面卡模式
對於SimpleChannelInboundHandler中:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (acceptInboundMessage(msg)) {
@SuppressWarnings("unchecked")
I imsg = (I) msg;
channelRead0(ctx, imsg);
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (autoRelease && release) {
ReferenceCountUtil.release(msg);
}
}
}
protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;
因此我們繼承SimpleChannelInboundHandler後,處理入站的資料我們只需要重新實現channelRead0方法,當channelRead真正被呼叫的時候我們的邏輯才會被處理。這裡使用的是模板模式,讓主要的處理邏輯保持不變,讓變化的步驟通過介面實現來完成
值得注意的是對於SimpleChannelInboundHandler入站的資料,當被讀取之後可能會執行ReferenceCountUtil.release(msg)釋放資源。底層是實現ReferenceCounted,當新的物件初始化的時候計數為1,retain()方法實現其他地方的引用計數加1,release()方法實現應用減一,當計數減少到0的時候會被顯示清除,再次訪問被清除的物件會出現訪問衝突。因此,當我們實現自己的Handler的時候如果希望將客戶端傳送過來的資料傳送到客戶端,可能在上述finally中已經釋放了資源(writeAndFlush是非同步處理),所以會出現異常情況。
但是當我們實現的是ChannelInboundHandler類的時候,重寫channelRead方法時,需要釋放ByteBuf相關的記憶體,可以使用Netty提供了一個工具方法,ReferenceCountUtil.release()