Netty 解析 JSON 格式报文数据
解决
Netty 框架自带了 JsonObjectDecoder
JSON 对象解码器,它属于在四种常见的解码器之外的解码器。除此之外,还有 base64 解码器、xml 解码器等。可以在 netty-codec
包下找到所有 netty 支持的解码器。
pipeline.addLast(new JsonObjectDecoder(4096))
.addLast(new StringDecoder())
.addLast(new StringEncoder())
.addLast(new HDTcpSocketHandler());
其中 4096 代表 1024 * 4,使用 UTF-8 编码,则一个字符占用四个字节,即限定最多解析 1024 个字符长度的 JSON 字符串。
HDTcpSocketHandler
是自定义的消息处理器,其他两个字符串编码器、解码器属于正常处理,特别加的 JsonObjectDecoder
是为了解决沾包、半包(拆包)问题的。
它的本质是通过判断 json 对象中的分割符来分割多个 json 字符串,然后将分割后的 json 字符串存入 ByteBuf 中输出。
Netty 处理消息主要步骤
1. 首先是启动 NettyServer
官方文档 4.x 用户指南 里使用 main 方法去启动,而在 Spring 中则直接注册为 @Component
,使用另开一个线程去初始化,主要是为了让主进程继续加载其他的服务。
@PostConstruct
public void init() {
log.debug("Netty Server init");
// initNetty();
new Thread(this::initNetty).start();
}
初始化中 childHandler
设置 ChannelInitializer
通道初始化。
2. ChannelInitializer 通道初始化
官网的 demo 是直接绑定消息处理器,但这里需要额外空闲状态读写超时时间和超时处理(关闭通道和会话)。
pipeline.addLast("counter", counterHandler)
.addLast("myDispatch", new ProtocolDecoder())
.addLast(new IdleStateHandler(150, 150, 150))
.addLast(new HeartbeatHandler());
3. 按照不同的通讯协议分别添加响应的编码器和解码器
目前接入了 5 个厂家,每个厂家都有自己的通讯协议,而每个通讯协议都有自己的特别的编码格式。有根据字节码头几位判断的,也有使用 mqtt 协议的,还有结尾为换行符或者特殊字符的。
这里分别使用了 LengthFieldBasedFrameDecoder
(基于长度的域解码器)、LineBasedFrameDecoder
行帧解码器、DelimiterBasedFrameDecoder
固定分隔符解码器,还有一种 FixedLengthFrameDecoder
固定长度编码器暂时未使用到。这四种解码器就是常见的可以解决沾包、半包问题的四种类型解码器。
其他的,基于特殊格式的解码器,有 JsonObjectDecoder
JSON 对象解码器,XmlFrameDecoder
xml 解码器等。
当然,特别的还有自己通讯协议的,比如 WebSocket
、Http
、Mqtt
等。这些特殊协议有自己的框架边界,所以不会出现沾包、半包的问题。
后记
本来一开始没有找到这个 JsonObjectDecoder
JSON 对象解码器,是准备找找教程,自定义一个解码器来识别 JSON 格式编码的,看了 JSON 对象解码器才感觉到算法的复杂性。
另外,几种解码器都有一个 maxLength
最大长度,超过最大长度就放弃识别,表示识别失败,这也是非常必要的,不然如果出现非预期的报文,会一直在那边等待结束符或者整体匹配格式,可能导致后续正常的报文丢失和通道的拥堵。