Netty的ChannelHandler大全
Handler是Netty的一個重要組件,涉及數據的各種業務處理操作以及數據包的編碼與解碼。本文對一些常見的ChannelHandler做簡要的概述和一些說明和使用場景,不做全面的詳細闡述。
1. ChannelInboundHandler接口
public interface ChannelInboundHandler extends ChannelHandler {
//...略其他方法
void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
@Override
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
Netty中所有的入站處理器都實現了ChannelInboundHandler接口,上面的部分源碼羅列了該接口最常被覆寫的方法。
2. ChannelInboundHandlerAdapter類
public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {
// ...略其他方法
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.fireChannelRead(msg);
}
}
ChannelInboundHandlerAdapter類是Netty爲開發者提供的默認入站處理器適配器實現類,該類實現了ChannelInboundHandler接口,開發者在自定義入站處理器時,只需通過繼承此類並複寫channelRead方法就可以完成自定義的入站處理邏輯。
public class AdapterTest extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf=(ByteBuf)msg;
byte[] bytes=new byte[buf.readableBytes()];
buf.getBytes(0,bytes);
System.out.println("入站數據:"+new String(bytes, CharsetUtil.UTF_8));
super.channelRead(ctx, msg); //調用父類的channelRead方法(會默認添加)
}
}
super.channelRead(ctx,msg) 調用父類的方法ctx.fireChannelRead(msg),觸發通道可讀事件,交由pipeline的下一個出站處理器處理。如果刪去這一調用,消息會被截斷,也即截停在這個出站處理器,下一入站處理器不會讀取到這個消息。
3. SimpleChannelInboundHandler抽象類
public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter {
//..略其他方法和屬性
private final boolean autoRelease; //開啓自動釋放緩存
protected SimpleChannelInboundHandler() {
this(true);
}
protected SimpleChannelInboundHandler(boolean autoRelease) {
matcher = TypeParameterMatcher.find(this, SimpleChannelInboundHandler.class, "I");
this.autoRelease = autoRelease;
}
@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(以下簡稱S)是一個泛型抽象類,繼承了ChannelInboundHandlerAdapter(以下簡稱A),也被常用作自定義的入站處理器的父類,但它與ChannelInboundHandlerAdapter有幾點不同:
-
S類可以指定消息泛型,支持處理泛型數據,而A類不支持
-
S類作爲父類時,需要覆寫channelRead0方法,A類作爲父類時,需要覆寫channelRead方法
-
S類默認是會自動釋放緩衝區內存的,當ByteBuf被釋放,消息將不會被進一步傳遞(與A類中不調用super方法類似)
因此,SimpleChannelInboundHandler常被用來做自動釋放,但也可以通過構造方法指定autoRelease=false關閉自動釋放功能。
4. ByteToMessageDecoder解碼器
編碼器首先也是一個入站處理器,ByteToMessageDecoders作爲解碼器的抽象父類,並不涉及具體的解碼實現,僅僅提供了一個解碼的流程框架。
public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
//..略其他方法屬性
}
經由ByteToMessageDecoder解碼後的消息,傳遞給下一站的不再是ByteBuf,該類會調用方法將緩衝區的引用-1,進而釋放緩衝區內存,解碼後得出的Java POJO(List<Object> out)作爲入站消息傳遞給下一個入站處理器。
5. ReplayingDecoder解碼器
public abstract class ReplayingDecoder<S> extends ByteToMessageDecoder {
private S state; //狀態類型
private int checkpoint = -1; //讀斷點指針位置
//..略其他方法屬性
}
ReplayingDecoder抽象類(以下簡稱R類)繼承自ByteToMessageDecoder(以下簡稱B類),R類在內部定義了一個新的二進制緩衝區裝飾類,ReplayingDecoderBuffer,在緩衝區真正讀取數據之前,首先會進行長度判斷。除了長度判斷之外,ReplayingDecoder還可用來解決TCP粘包/半包問題,但是由於性能一般且並不是所有的ByteBuf操作都被ReplayingDecoderBuffer支持。所以這個類實際開發中使用的也不多。
6. MessageToMessageDecoder解碼器
public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter {}
MessageToMessageDecoder繼承自ChannelInboundHandlerAdapter,可以將一個Java POJO對象解碼爲另外一個Java POJO對象,是個抽象泛型類,實現時需要指定入站消息類型。
7. FixenLengthFrameDecoder解碼器
public class FixedLengthFrameDecoder extends ByteToMessageDecoder {
private final int frameLength;
public FixedLengthFrameDecoder(int frameLength) {
if (frameLength <= 0) {
throw new IllegalArgumentException(
"frameLength must be a positive integer: " + frameLength);
}
this.frameLength = frameLength;
}
固定長度數據包解碼器,適用於每個接受到的數據包的長度都是固定的。
8. LineBasedFrameDecoder解碼器
public class LineBasedFrameDecoder extends ByteToMessageDecoder {}
行分割數據包解碼器,適用於每個ByteBuf數據包使用換行符(\n)或者回車換行符(\r\n)作爲數據包的邊界分割。
9. DelimiterBasedFrameDecoder解碼器
public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
//略
int maxFrameLength, //解碼的數據包的最大長度
boolean stripDelimiter, //解碼後數據包是否去掉分隔符,一般選擇是
ByteBuf delimiter //分隔符
)
自定義分隔符數據包解碼器,與行分割數據包解碼器類似,但更通用,可以自己定義分隔符。
10. LegnthFieldFrameDecoder解碼器
public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {
int maxFrameLength, //發送的數據包的最大長度
int lengthFieldOffset, //長度字段的偏移量,即長度字段位於數據包內部數組的起始下標
int legnthFieldLength, // 長度字段所佔的字節數,int爲4 short或char爲2
int lengthAdjustment, //矯正值計算公式:內容字段偏移-長度字段偏移-長度字段的字節(內容與長度間隔的長度)
int initialBytesToStrip //丟棄的起始字節數。
)
自定義長度數據包解碼器,長度靈活,可以自行設定,在一般的基於Header-Content協議的內容傳輸,都會使用這個解碼器進行解碼。該解碼器的5個重要屬性如代碼所示。
數據報構成 | 版本 | 長度 | 魔數 | content |
---|---|---|---|---|
所佔字節 | 2字節 | 4字節 | 4字節 | 50字節 |
如上例子,lengthFileldOffset=2,lengthFiledLength=4,lengthAdujstment=4,initialBytesToStrip=10
11.ByteToMessageCodec編解碼器
編解碼器 也即編碼器跟解碼器都實現在了一個類中。
public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler {
//..略
private final MessageToByteEncoder<I> encoder;
private final ByteToMessageDecoder decoder ;
}
ByteToMessageCodec是一個抽象的泛型類,繼承時需要制定泛型I,I即編碼時的Java POJO對象的數據類型。
12. Http相關
-
HttpRequestEncoder 請求出站編碼器,用於HTTP客戶端
-
HttpRequestDeocoder 請求入解碼碼器,用於HTTP服務器
-
HttpResponseEncoder 響應出站編碼器,用戶HTTP服務器
-
HttpResponseDecoder 響應入站解碼器,用戶HTTP客戶端
-
HttpClientCodec 客戶端編解碼器
-
HttpServerCodec 服務端編解碼器
-
HttpObjectAggregator 消息聚合,將一個HttpMessage和跟隨它額度多個HttpContent聚合爲單個FullHttpRequest/FullHttpResponse
-
HttpContentDecompressor HTTP消息解壓
-
HttpContentComporessor HTTP消息壓縮