Netty 內(nèi)置編解碼器
1. 前言
上節(jié)我們講解了 Netty 的內(nèi)置編碼器以及自定義編碼器,本節(jié)主要講解 Netty 提供的幾個核心編解碼器的抽象類,主要是 MessageToByteEncoder
、ByteToMessageDecoder
、SimpleChannelInboundHandler
。
2. 學習目的
Netty 官方也是考慮到了如何減輕開發(fā)人員的繁瑣、重復性的工作,因此,它內(nèi)置了一些好用的編解碼器抽象,讓我們更加便捷的自定義自己想要的編解碼器。
通過本節(jié)學習,我們需要掌握以下幾點
- 有哪些編解碼器的抽象;
- 它們的核心原理是什么。
3. 類關系圖
4. MessageToByteEncoder
從字面意思上可知,它主要是把消息內(nèi)容轉(zhuǎn)換成 Byte,也就是說是編碼。使用非常的簡單,繼承 MessageToByteEncoder
可以很容易的開發(fā)一個 Handler。
實例:
public class MyEncoder extends MessageToByteEncoder<BaseBean> {
protected void encode(ChannelHandlerContext channelHandlerContext,
BaseBean baseBean,
ByteBuf byteBuf) throws Exception {
//1.把“數(shù)據(jù)”轉(zhuǎn)換成字節(jié)數(shù)組
byte[] bytes= JSON.toJSONBytes(baseBean);
//2.把字節(jié)數(shù)組往ByteBuf容器寫
byteBuf.writeBytes(bytes);
}
}
ch.pipeline().addLast(new MyEncoder());
源碼:保留核心代碼
public abstract class MessageToByteEncoder<I> extends ChannelOutboundHandlerAdapter {
//類型匹配器
private final TypeParameterMatcher matcher;
//構造函數(shù)
protected MessageToByteEncoder(boolean preferDirect) {
//初始化
this.matcher = TypeParameterMatcher.find(this, MessageToByteEncoder.class, "I");
this.preferDirect = preferDirect;
}
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
ByteBuf buf = null;
if (this.acceptOutboundMessage(msg)) {
//類型判斷通過,則處理
I cast = msg;
//創(chuàng)建ByteBuf
buf = this.allocateBuffer(ctx, msg, this.preferDirect);
//調(diào)用抽象方法(由子類實現(xiàn))
this.encode(ctx, cast, buf);
} else {
//類型判斷不通過,則往下流轉(zhuǎn)
ctx.write(msg, promise);
}
}
//抽象方法
protected abstract void encode(ChannelHandlerContext var1, I var2, ByteBuf var3) throws Exception;
}
5. ByteToMessageDecoder
從字面上我們也很容易猜到它的作用,主要是把 Byte 類型的數(shù)據(jù)轉(zhuǎn)換成對應實體,也稱之為解碼。使用非常的簡單。
實例:
public class MyDecoder extends ByteToMessageDecoder {
//把ByteBuf反序列化,并且添加到List里面
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) {
//1.定義byte[],長度為ByteBuf可讀長度
byte[] bytes=new byte[byteBuf.readableBytes()];
//2.往byte[]讀取數(shù)據(jù)
byteBuf.readBytes(bytes);
//3.對象流
ByteArrayInputStream is=new ByteArrayInputStream(bytes);
ObjectInputStream iss=new ObjectInputStream(is);
User user=(User)iss.readObject();
//4.關閉流
is.close();
iss.close();
//5.添加到集合
list.add(user);
}
}
繼承了 ByteToMessageDecoder
這個類之后,我們只需要實現(xiàn)一下 decode () 方法,這里的 in 大家可以看到,傳遞進來的時候就已經(jīng)是 ByteBuf 類型,所以我們不再需要強轉(zhuǎn),第三個參數(shù)是 List 類型,我們通過往這個 List 里面添加解碼后的結(jié)果對象,就可以自動實現(xiàn)結(jié)果往下一個 handler 進行傳遞,我們就實現(xiàn)了解碼的邏輯 handler。
6. SimpleChannelInboundHandler
前面講解 ChannelHandler 多業(yè)務情況下的時候,我們講解到了 SimpleChannelInboundHandler
,它的核心作用是自動判斷數(shù)據(jù)格式類型,并且轉(zhuǎn)發(fā)給對應的 Handler 來處理。
一般來說,Netty 開發(fā)的應用如果很復雜的時候,那么應該如何處理呢?通常有三種方案。
6.1 方案一
對反序列化后的結(jié)果進行類型判斷,不同的類型做不同的業(yè)務處理。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//對反序列化后的結(jié)果進行類型判斷,不同的類型做不同的業(yè)務處理
if(msg instanceof LoginReqBean){
login((LoginReqBean) msg,ctx.channel());
}else if(msg instanceof MsgReqBean){
sendMsg((MsgReqBean)msg,ctx.channel());
}
}
這種模式比較簡單,但是通過 if else
邏輯進行邏輯的處理,當我們要處理的指令越來越多的時候,代碼會顯得越來越臃腫。
6.2 方案二
判斷是否是自己應該處理,如果不是,則手工往下流轉(zhuǎn)。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof LoginReqBean){
//業(yè)務處理
}else{
//往下流轉(zhuǎn)
ctx.fireChannelRead(msg);
}
}
6.3 方案三
使用 SimpleChannelInboundHandler
來簡化我們的指令處理邏輯。
SimpleChannelInboundHandler
使用非常簡單,我們在繼承這個類的時候,給他傳遞一個泛型參數(shù),然后在 channelRead0 () 方法里面,我們不用再通過 if 邏輯來判斷當前對象是否是本 handler 可以處理的對象,也不用強轉(zhuǎn),不用往下傳遞本 handler 處理不了的對象,這一切都已經(jīng)交給父類 SimpleChannelInboundHandler
來實現(xiàn)了,我們只需要專注于我們要處理的業(yè)務邏輯即可。
實例:
public class LoginReqHandler extends SimpleChannelInboundHandler<LoginReqBean> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, LoginReqBean loginReqBean){
//登錄邏輯
}
}
public class MsgReqHandler extends SimpleChannelInboundHandler<MsgReqBean> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, MsgReqBean msgReqBean){
//消息發(fā)送邏輯
}
}
源碼:只保留核心部分
public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter {
//類型匹配器
private final TypeParameterMatcher matcher;
protected SimpleChannelInboundHandler(boolean autoRelease) {
//初始化類型匹配器
this.matcher = TypeParameterMatcher.find(this, SimpleChannelInboundHandler.class, "I");
}
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (this.acceptInboundMessage(msg)) {
//類型校驗通過通過,則調(diào)用抽象方法(子類去實現(xiàn))
this.channelRead0(ctx, msg);
} else {
//類型校驗不通過,則往下流轉(zhuǎn)
ctx.fireChannelRead(msg);
}
}
//抽象方法,由自定義業(yè)務類去實現(xiàn)
protected abstract void channelRead0(ChannelHandlerContext var1, I var2) throws Exception;
}
7. 小結(jié)
本節(jié)學習主要掌握以下知識點
- 基于 MessageToByteEncoder,我們可以實現(xiàn)自定義編碼,而不用關心 ByteBuf 的創(chuàng)建,不用每次向?qū)憯?shù)據(jù)的時候,都手工進行編碼;
- 基于 ByteToMessageDecoder,我們可以實現(xiàn)自定義解碼,而不用關心 ByteBuf 的強轉(zhuǎn)和 解碼結(jié)果的傳遞;
- 基于 SimpleChannelInboundHandler,我們可以實現(xiàn)根據(jù)數(shù)據(jù)格式來判斷由哪個 Handler 去處理,不需要手工
if else
判斷,不需要手動傳遞對象,做到了真正關心業(yè)務邏輯的處理; - 其實,這三種 Handler 也是有各自的應用場景,
ByteToMessageDecoder
和MessageToByteEncoder
是用來封裝解碼器和編碼器,SimpleChannelInboundHandler
則是用于業(yè)務邏輯的簡化開發(fā)。