參考https://blog.csdn.net/u011262847/article/details/78713881
每一個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()
如果在channelRead中寫了ctx.write(接收到的內容),由於write是異步的,可能在channelRead返回之后,仍然沒有完成,為此,擴展了ChannelInboundHandlerAdapter,其在這個時間點上不會釋放消息,消息在channelReadComplete(),當writeAndFlush方法被調用時釋放。