关于netty:netty系列之netty中的懒人编码解码器

31次阅读

共计 5728 个字符,预计需要花费 15 分钟才能阅读完成。

简介

netty 之所以弱小,是因为它内置了很多十分有用的编码解码器,通过应用这些编码解码器能够很不便的搭建出十分弱小的应用程序,明天给大家讲讲 netty 中最根本的内置编码解码器。

netty 中的内置编码器

在对 netty 的包进行引入的时候,咱们能够看到 netty 有很多以 netty-codec 结尾的 artifactId,统计一下,有这么多个:

netty-codec
netty-codec-http
netty-codec-http2
netty-codec-memcache
netty-codec-redis
netty-codec-socks
netty-codec-stomp
netty-codec-mqtt
netty-codec-haproxy
netty-codec-dns

总共 10 个 codec 包,其中 netty-codec 是最根底的一个,其余的 9 个是对不同的协定包进行的扩大和适配,能够看到 netty 反对罕用的和风行的协定格局,十分的弱小。因为 codec 的内容十分多,要解说他们也不是很容易,本文将会以 netty-codec 做一个例子,解说其中最根本的也是最通用的编码解码器。

应用 codec 要留神的问题

尽管 netty 提供了很不便的 codec 编码解码器,然而正如咱们在前一篇文章中提到的,有些 codec 是须要和 Frame detection 一起配合应用的,先应用 Frame detection 将 ByteBuf 拆分成一个个代表实在数据的 ByteBuf,再交由 netty 内置的 codec 或者自定义的 codec 进行解决,这样能力起到应有的成果。

netty 内置的根本 codec

netty 中根本的 codec 有 base64、bytes、compression、json、marshalling、protobuf、serialization、string 和 xml 这几种。

上面将会一一进行解说。

base64

这个 codec 是负责 ByteBuf 和 base64 过后的 ByteBuf 之间的转换。尽管都是从 ByteBuf 到 ByteBuf,然而其中的内容产生了变动。

有两个要害的类,别离是 Base64Encoder 和 Base64Decoder。因为 Base64Decoder 是一个 MessageToMessageDecoder,所以须要应用一个 DelimiterBasedFrameDecoder 提前进行解决,罕用的例子如下:

   ChannelPipeline pipeline = ...;
  
   // Decoders
   pipeline.addLast("frameDecoder", new DelimiterBasedFrameDecoder(80, Delimiters.nulDelimiter()));
   pipeline.addLast("base64Decoder", new Base64Decoder());
  
   // Encoder
   pipeline.addLast("base64Encoder", new Base64Encoder());

bytes

bytes 是将 bytes 数组和 ByteBuf 之间进行转换,同样的在 decode 之前,也须要应用 FrameDecoder,通常的应用形式如下:

   ChannelPipeline pipeline = ...;
  
   // Decoders
   pipeline.addLast("frameDecoder",
                    new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
   pipeline.addLast("bytesDecoder",
                    new ByteArrayDecoder());
  
   // Encoder
   pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
   pipeline.addLast("bytesEncoder", new ByteArrayEncoder());
   

compression

compression 这个包的内容就比拟丰盛了,次要是对数据的压缩和解压缩服务。其反对的算法如下:

brotli
Bzip2
FastLZ
JdkZlib
Lz4
Lzf
Snappy
Zlib
Zstandard

compression 对于大数据量的传输特地有帮忙,通过压缩能够节俭传输的数据量,从而进步传输速度。

然而压缩是应用特定的算法来计算的,所以它是一个高 CPU 的操作,咱们在应用的时候须要兼顾网络速度和 CPU 性能,并从中失去均衡。

json

json 这个包外面只有一个 JsonObjectDecoder 类,次要负责将 Byte 流的 JSON 对象或者数组转换成 JSON 对象和数组。

JsonObjectDecoder 间接就是一个 ByteToMessageDecoder 的子类,所以它不须要 FrameDecoder,它是依据括号的匹配来判断 Byte 数组的起始地位,从而辨别哪些 Byte 数据是属于同一个 Json 对象或者数组。

咱们如果心愿应用 JSON 来传输数据的话,这个类就十分有用了。

marshalling

Marshalling 的全称叫做 JBoss Marshalling,它是 JBoss 出品的一个对象序列化的形式,然而 JBoss Marshalling 的最新 API 还是在 2011-04-27,曾经有 10 年没更新了,是不是曾经被废除了?

所以这里咱们不具体介绍这个序列化的内容,感兴趣的小伙伴能够自行摸索。

protobuf

protobuf 大家应该都很相熟了,它是 google 出品的一种信息替换格局,能够将其看做是一种序列化的形式。它是语言中立、平台中立、可扩大的结构化数据序列化机制,和 XML 相似,然而比 XML 更小、更快、更简略。

netty 对 protobuf 的反对在于能够将 protobuf 中的 message 和 MessageLite 对象跟 ByteBuf 进行转换。

protobuf 的两个编码器也是 message 到 message 间接的转换,所以也须要应用 frame detection。当然你也能够应用其余的 frame detection 比方 LengthFieldPrepender 和 LengthFieldBasedFrameDecoder 如下所示:

   ChannelPipeline pipeline = ...;
  
   // Decoders
   pipeline.addLast("frameDecoder",
                    new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
   pipeline.addLast("protobufDecoder",
                    new ProtobufDecoder(MyMessage.getDefaultInstance()));
  
   // Encoder
   pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
   pipeline.addLast("protobufEncoder", new ProtobufEncoder());

其中 LengthFieldPrepender 会主动给字段后面加上一个长度字段:

 之前:+----------------+
   | "HELLO, WORLD" |
   +----------------+

之后:+--------+----------------+
   + 0x000C | "HELLO, WORLD" |
   +--------+----------------+

当然 netty 为 protobuf 筹备了两个专门的 frame detection,他们是 ProtobufVarint32FrameDecoder 和 ProtobufVarint32LengthFieldPrepender。在解说这两个类之前,咱们须要理解一下 protobuf 中的 Base 128 Varints。

什么叫 Varints 呢?就是序列化整数的时候,占用的空间大小是不一样的,小的整数占用的空间小,大的整数占用的空间大,这样不必固定一个具体的长度,能够缩小数据的长度,然而会带来解析的复杂度。

那么怎么晓得这个数据到底须要几个 byte 呢?在 protobuf 中,每个 byte 的最高位是一个判断位,如果这个位被置位 1,则示意前面一个 byte 和该 byte 是一起的,示意同一个数,如果这个位被置位 0,则示意前面一个 byte 和该 byte 没有关系,数据到这个 byte 就完结了。

举个例子,一个 byte 是 8 位,如果示意的是整数 1,那么能够用上面的 byte 来示意:

0000 0001

如果一个 byte 装不下的整数,那么就须要应用多个 byte 来进行连贯操作,比方上面的数据表示的是 300:

1010 1100 0000 0010

为什么是 300 呢?首先看第一个 byte,它的首位是 1,示意前面还有一个 byte。再看第二个 byte,它的首位是 0,示意到此就完结了。咱们把判断位去掉,变成上面的数字:

010 1100 000 0010

这时候还不能计算数据的值,因为在 protobuf 中,byte 的位数是反过来的,所以咱们须要把下面的两个 byte 替换一下地位:

000 0010 010 1100 

也就是:

10 010 1100 

=256 + 32 + 8 + 4 = 300

在 protobuf 中个别应用 Varint 作为字段的长度位,所以 netty 提供了 ProtobufVarint32LengthFieldPrepender 和 ProtobufVarint32FrameDecoder 对 ByteBuf 进行转换。

比方为 ByteBuf 增加 varint 的 length:

   BEFORE ENCODE (300 bytes)       AFTER ENCODE (302 bytes)
   +---------------+               +--------+---------------+
   | Protobuf Data |-------------->| Length | Protobuf Data |
   |  (300 bytes)  |               | 0xAC02 |  (300 bytes)  |
   +---------------+               +--------+---------------+

解码的时候删除 varint 的 length 字段:

   BEFORE DECODE (302 bytes)       AFTER DECODE (300 bytes)
   +--------+---------------+      +---------------+
   | Length | Protobuf Data |----->| Protobuf Data |
   | 0xAC02 |  (300 bytes)  |      |  (300 bytes)  |
   +--------+---------------+      +---------------+

serialization

序列化就是把对象转换成二进制数据,事实上所有的 codec 都能够成为序列化。他们提供了对象和 byte 之间的转换方法。

netty 也提供了两个对象的转换方法:ObjectDecoder 和 ObjectEncoder。

要留神的是,这两个对象和 JDK 自带的 ObjectInputStream 和 ObjectOutputStream,是不兼容的,如果要兼容,能够应用 CompactObjectInputStream、CompactObjectOutputStream 和 CompatibleObjectEncoder。

string

String 是咱们最常应用到的对象,netty 为 string 提供了 StringDecoder 和 StringEncoder。

同样的,在应用这两个类之前,须要将音讯进行转换,通常应用的是 LineBasedFrameDecoder 按行进行转换:

   ChannelPipeline pipeline = ...;
  
   // Decoders
   pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(80));
   pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
  
   // Encoder
   pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));

xml

xml 也是一个十分罕用的格局,然而它的体积会比拟大,当初应该用的比拟少了。netty 提供了一个 XmlFrameDecoder 来进行解析。

因为 xml 有本人的开始和结束符,所以不须要再做 frame detection,间接转换即可,如:

   +-----+-----+-----------+
   | <an | Xml | Element/> |
   +-----+-----+-----------+
转换成:+-----------------+
   | <anXmlElement/> |
   +-----------------+
   +-----+-----+-----------+-----+----------------------------------+
   | <an | Xml | Element/> | <ro | ot><child>content</child></root> |
   +-----+-----+-----------+-----+----------------------------------+
   转换成:+-----------------+-------------------------------------+
   | <anXmlElement/> | <root><child>content</child></root> |
   +-----------------+-------------------------------------+

都是能够的。

总结

netty 提供了很多优良的 codec 来适配各种利用协定,大家能够多用用,找找不同协定的不同之处。

本文已收录于 http://www.flydean.com/16-netty-buildin-codec-common/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

正文完
 0