netty 系列之:netty 中的懒人编码解码器
简介
netty 之所以强大,是因为它内置了很多非常有用的编码解码器,通过使用这些编码解码器可以很方便的搭建出非常强大的应用程序,今天给大家讲讲 netty 中最基本的内置编码解码器。
netty 中的内置编码器
在对 netty 的包进行引入的时候,我们可以看到 netty 有很多以 netty-codec 开头的 artifactId,统计一下,有这么多个:
总共 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 提前进行处理,常用的例子如下:
bytes
bytes 是将 bytes 数组和 ByteBuf 之间进行转换,同样的在 decode 之前,也需要使用 FrameDecoder,通常的使用方式如下:
compression
compression 这个包的内容就比较丰富了,主要是对数据的压缩和解压缩服务。其支持的算法如下:
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 如下所示:
其中 LengthFieldPrepender 会自动给字段前面加上一个长度字段:
当然 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 来表示:
如果一个 byte 装不下的整数,那么就需要使用多个 byte 来进行连接操作,比如下面的数据表示的是 300:
为什么是 300 呢?首先看第一个 byte,它的首位是 1,表示后面还有一个 byte。再看第二个 byte,它的首位是 0,表示到此就结束了。我们把判断位去掉,变成下面的数字:
这时候还不能计算数据的值,因为在 protobuf 中,byte 的位数是反过来的,所以我们需要把上面的两个 byte 交换一下位置:
也就是:
=256 + 32 + 8 + 4 = 300
在 protobuf 中一般使用 Varint 作为字段的长度位,所以 netty 提供了 ProtobufVarint32LengthFieldPrepender 和 ProtobufVarint32FrameDecoder 对 ByteBuf 进行转换。
比如为 ByteBuf 添加 varint 的 length:
解码的时候删除 varint 的 length 字段:
serialization
序列化就是把对象转换成二进制数据,事实上所有的 codec 都可以成为序列化。他们提供了对象和 byte 之间的转换方法。
netty 也提供了两个对象的转换方法:ObjectDecoder 和 ObjectEncoder。
要注意的是,这两个对象和 JDK 自带的 ObjectInputStream 和 ObjectOutputStream,是不兼容的,如果要兼容,可以使用 CompactObjectInputStream、CompactObjectOutputStream 和 CompatibleObjectEncoder。
string
String 是我们最常使用到的对象,netty 为 string 提供了 StringDecoder 和 StringEncoder。
同样的,在使用这两个类之前,需要将消息进行转换,通常使用的是 LineBasedFrameDecoder 按行进行转换:
xml
xml 也是一个非常常用的格式,但是它的体积会比较大,现在应该用的比较少了。netty 提供了一个 XmlFrameDecoder 来进行解析。
因为 xml 有自己的开始和结束符,所以不需要再做 frame detection,直接转换即可,如:
都是可以的。
总结
netty 提供了很多优秀的 codec 来适配各种应用协议,大家可以多用用,找找不同协议的不同之处。
本文已收录于 http://www.flydean.com/16-netty-buildin-codec-common/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
版权声明: 本文为 InfoQ 作者【程序那些事】的原创文章。
原文链接:【http://xie.infoq.cn/article/4535f36576f90f4e77a7f4808】。文章转载请联系作者。
评论