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