编码器: encoder , 出站handler;
解码器: decoder, 入站handler。
用途包括:POP3
,IMAP
,SMTP
协议。(邮件服务器)
资源管理
编码器和解码器的消息被消费后,会自动调用ReferenceCountUtil.release(message)
进行释放。
如果要阻止这种自动释放,可以显式调用ReferenceCountUtil.retain(message)
保留消息。(后续再自己手动释放)
解码器
两种:
1.字节=>消息:ByteToMessageDecoder
,ReplayingDecoder
;
2.消息=>消息:MessageToMessageDecoder
。(不需要检查readableBytes
)
解码器包括:
1 | // 1.字节=>消息: (解码) |
相应的声明:
1 | public class LineBasedFrameDecoder extends ByteToMessageDecoder; |
ByteToMessageDecoder(抽象类)
处理流程是字节=>消息,也就是ByteBuf in
=>List<Object>out
。
需要注意每次从in
读之前,需要确认可读字节数量:in.readableBytes()
。
具体代码:
1 | public class ToIntegerDecoder extends ByteToMessageDecoder { |
ByteToMessageDecoder
有俩api:
1 | decode: 必须实现,解析每条消息; |
ReplayingDecoder(抽象类)(尽量不用)
使用上比ByteToMessageDecoder
省略readableBytes
的调用,但是速度稍慢。
1 | public class ToIntegerDecoder2 extends ReplayingDecoder<Void> { |
尽可能使用ByteToMessageDecoder
而不是ReplaingDecoder
。
两个原因:
ReplaingDecoder
比较慢;- 内部用的是
ReplayingDecoderByteBuf
,并没有支持所有ByteBuf
的操作。(api不全)
MessageToMessageDecoder(抽象类)
消息=>消息。
如果声明是MessageToMessageDecoder<Integer>
,那么输入就是Integer
类型。
具体签名是:
1 | public abstract class MessageToMessageDecoder<I> |
实际案例:
1 | public class IntegerToStringDecoder extends |
可以发现这种消息转换不需要检查readableBytes
。(毕竟输入已经不是byte
了)
TooLongFrameException类
解码前,解码器会缓冲大量的数据。如果发现缓冲太多,可以抛出异常来报告这种情况:
1 | public class SafeByteToMessageDecoder extends ByteToMessageDecoder { |
抛异常的用意:
1.可能是上游生产太快,因此需要识别这种情况;
2.可能是下游消费太慢,也需要识别这种情况。
后续可以用exceptionCaught
来捕获这个异常,可能的处理包括:
1.向生产端返回一个特殊的响应;
如HTTP:413
错误 – 请求实体太大 (Request entity too large)414
错误 – 请求 URI 过长 (Request URI too long)
2.关闭对应的连接。
3.其他处理方案。
编码器
两种:
1.消息=>字节:MessageToByteEncoder;
2.消息=>消息:MessageToMessageEncoder。
// 主要是入站出站方向不同,不然和前面的消息转换差不多。
编码器包括:
1 | MessageToByteEncoder<I>: 消息=>字节 |
具体代码:
1 | public class IntegerToStringEncoder |
解码器:decode
,decodeLast
编码器:encode
编解码器(编码器+解码器复合)
编解码复合,也有两种:
- 字节<=>消息:
ByteToMessageCodec
; - 消息<=>消息:
MessageToMessageCodec
.
可以发现就是多了一个Codec
后缀。
ByteToMessageCodec(抽象类)
ByteToMessageCodec
的方法就是把编码器和解码器的api都加上:
1 | decode(ctx,ByteBuf in,List<Object>out) |
MessageToMessageCodec(抽象类)
这是一个参数化的类,声明如下;
1 | public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends ChannelDuplexHandler |
看到这里大致可以发现一个规律,这些编码器、解码器最重要的是定义输入数据的数据类型,而输出数据的数据类型一般都是List<Object>
。MessageToMessageCodec
本质上是一个可以处理入站事件和出站事件的handler,因此需要定义入站和出站的数据类型:
1 | INBOUND_IN: 入站数据参数的数据类型 |
它的两个接口:
1 | protected abstract void encode(ChannelHandlerContext ctx |
CombinedChannelDuplexHandler类
编解码器是从头写一个,实现双向转换。CombinedChannelDuplexHandler
是从已经写好的编码器和解码器,生成一个双向转换的封装类。
声明:
1 | public class CombinedChannelDuplexHandler<I extends ChannelInboundHandler |
传入两个类,I是inbound,O是outbound。具体样例如下:
1 | public class CombinedByteCharCodec extends |
传进来后调用父类默认写好的构造函数即可。
个人理解编解码器和DuplexHandler类都是高级用法,不一定实用。
个人偏好直接使用编码器、解码器就好了。