当前位置: 首页 > 后端技术 > Java

Netty系列:Netty中的LazyCodecs简介

时间:2023-04-02 10:12:43 Java

netty之所以强大,是因为它内置了很多非常好用的编解码器,通过使用这些编解码器,你可以轻松构建非常强大的应用程序,今天就来给大家讲讲netty中最基本的内置编解码器。在netty中内置编码器引入netty包的时候,我们可以看到netty有很多以netty-codec开头的artifactId。据统计,有这么几种:netty-codecnetty-codec-httpnetty-codec-http2netty-codec-memcachenetty-codec-redisnetty-codec-socksnetty-codec-stompnetty-codec-mqttnetty-codec-haproxynetty-codec-dns有一个一共10个编解码包,其中netty-codec是最基础的,另外9个是对不同协议包的扩展和适配,可以看出netty支持常用流行的协议格式,非常强大.因为codec的内容很多,不好解释。本文将以netty-codec为例,讲解最基础最常用的codec。使用codec需要注意的问题虽然netty提供了非常方便的codeccodec,但是我们在上一篇文章中提到,有些codec需要和Framedetection一起使用,首先使用Framedetection拆解ByteBuf,将其分成代表的ByteBufs真实的数据一个一个的处理,然后通过netty内置的codec或者自定义的codec进行处理,从而达到想要的效果。Netty内置的基本编解码器netty中的基本编解码器包括base64、bytes、compression、json、marshalling、protobuf、serialization、string和xml。下面将一一解释。base64codec负责ByteBuf与base64后ByteBuf的转换。虽然都是从ByteBuf到ByteBuf,但是内容变了。有两个关键类,Base64Encoder和Base64Decoder。因为Base64Decoder是一个MessageToMessageDecoder,所以需要提前使用一个DelimiterBasedFrameDecoder来处理。常见的例子如下:ChannelPipelinepipeline=...;//解码器pipeline.addLast("frameDecoder",newDelimiterBasedFrameDecoder(80,Delimiters.nulDelimiter()));pipeline.addLast("base64Decoder",newBase64Decoder());//编码器pipeline.addLast("base64Encoder",newBase64Encoder());bytesbytes是bytes数组和ByteBuf之间的转换,在解码之前,还需要用到FrameDecoder,通常的用法如下:ChannelPipelinepipeline=...;//解码器pipeline.addLast("frameDecoder",newLengthFieldBasedFrameDecoder(1048576,0,4,0,4));pipeline.addLast("bytesDecoder",newByteArrayDecoder());//编码器pipeline.addLast("frameEncoder",newLengthFieldPrepender(4));pipeline.addLast("bytesEncoder",newByteArrayEncoder());主要是数据压缩和解压缩服务。支持的算法如下:brotliBzip2FastLZJdkZlibLz4LzfSnappyZlibZstandardcompression对于传输大量数据特别有帮助。压缩可以节省传输的数据量,从而提高传输速度。但是压缩是用特定的算法计算出来的,所以是高CPU运算,我们在使用的时候需要兼顾网速和CPU性能,从中取得平衡。jsonjson包中只有一个JsonObjectDecoder类,主要负责将JSON对象或Byte流数组转换为JSON对象和数组。JsonObjectDecoder直接是ByteToMessageDecoder的子类,所以不需要FrameDecoder。它根据括号的匹配来判断Byte数组的起始位置,从而区分哪些Byte数据属于同一个Json对象或数组。如果我们想使用JSON传输数据,这个类非常有用。编组的全称是Marshalling,叫做JBossMarshalling。是JBoss出品的一种对象序列化方法。然而,JBossMarshalling的最新API仍在2011-04-27。10年没更新了。它被遗弃了吗?所以这里就不详细介绍本次连载的内容了,感兴趣的朋友可以自行探索。protobufprotobuf大家应该都不陌生了。是Google出品的一种信息交换格式,可以看成是一种序列化方式。它是一种语言中立、平台中立、可扩展的结构化数据序列化机制,类似于XML,但比XML更小、更快、更简单。Netty对protobuf的支持是可以将protobuf中的message和MessageLite对象转换为ByteBuf。protobuf的两个encoder也是直接从message到message的转换,所以也需要帧检测。当然你也可以使用其他的帧检测比如LengthFieldPrepender和LengthFieldBasedFrameDecoder如下:ChannelPipelinepipeline=...;//解码器pipeline.addLast("frameDecoder",newLengthFieldBasedFrameDecoder(1048576,0,4,0,4));pipeline.addLast("protobufDecoder",newProtobufDecoder(MyMessage.getDefaultInstance()));//编码器pipeline.addLast("frameEncoder",newLengthFieldPrepender(4));pipeline.addLast("protobufEncoder",newProtobufEncoder());LengthFieldPrepender会自动在字段前添加一个长度字段:before:+----------------+|“你好,世界”|+--------在--------+之后:+--------+----------------++0x000C|“你好,世界”|+--------+----------------+当然netty为protobuf准备了两个特殊的帧检测,它们是ProtobufVarint32FrameDecoder和ProtobufVarint32LengthFieldPrepender。在讲解这两个类之前,我们需要了解protobuf中的Base128Varints。什么是Varints?即序列化整数时,占用的空间不同。小整数占空间小,大整数占空间大。这样就不需要固定一个具体的长度,可以减少数据的长度,但是会带来Parsing的复杂度。那么你怎么知道这个数据需要多少字节呢?在protobuf中,每个字节的最高位是判断位。如果该位设置为1,表示下一个字节和本字节在一起,表示同一个数。如果该位设置为0,则表示下一个字节与该字节无关,数据到该字节结束。例如,一个字节是8位。如果表示整数1,可以用下面的字节表示:00000001如果一个字节不能装整数,那么就需要用多个字节进行连接操作,比如下面表示的数据是300:1010110000000010为什么是300?先看第一个字节,它的第一位是1,说明后面还有一个字节。再看第二个字节,它的第一位是0,表示结束。我们把判断位去掉,改成下面的数:01011000000010这个时候就不能计算出数据的值了,因为在protobuf中,字节数是倒过来的,所以我们需要把上面两个字节交换一下Position:00000100101100即:100101100=256+32+8+4=300protobuf中一般使用Varint作为字段的长度位,所以netty提供了ProtobufVarint32LengthFieldPrepender和ProtobufVarint32FrameDecoder来转换ByteBuf。例如将varint的长度加到ByteBuf中:BEFOREENCODE(300bytes)A??FTERENCODE(302bytes)+----------------++----------+-------------+|Protobuf数据|---------------->|长度|Protobuf数据||(300字节)||0xAC02|(300字节)|+----------------++--------+----------------+解码时删除varint长度字段:BEFOREDECODE(302bytes)A??FTERDECODE(300字节)+------+------------++---------------+|长度|Protobuf数据|----->|Protobuf数据||0xAC02|(300字节)||(300字节)|+--------+-------------++------------+serializationSerialization是将对象转换成二进制数据,实际上所有编解码器都可以序列化。它们提供对象和字节之间的转换方法。Netty还提供了两种对象的转换方法:ObjectDecoder和ObjectEncoder。需要注意的是,这两个对象与JDK自带的ObjectInputStream和ObjectOutputStream是不兼容的。如果要兼容,可以使用CompactObjectInputStream、CompactObjectOutputStream和CompatibleObjectEncoder。stringString是最常用的对象,netty为string提供了StringDecoder和StringEncoder。同样,在使用这两个类之前,需要对消息进行转换,通常使用LineBasedFrameDecoder按行转换:ChannelPipelinepipeline=...;//解码器pipeline.addLast("frameDecoder",newLineBasedFrameDecoder(80));pipeline.addLast("stringDecoder",newStringDecoder(CharsetUtil.UTF_8));//编码器pipeline.addLast("stringEncoder",newStringEncoder(CharsetUtil.UTF_8));xmlxml也是一种很常用的格式,但是它的体积会比较大,现在应该用的比较少了。netty提供了一个XmlFrameDecoder用于解析。因为xml有自己的起始符和结束符,不需要做帧检测,直接转换即可,如:+-----+-----+------------+|<一个|XML|元素/>|+-----+-----+------------+转化为:+------------------+||+----------------++-----+-----+-----------+-----+----------------------------------+|<一个|XML|元素/>|<罗|ot>内容|+-----+-----+------------+-----+------------------------------+转换为:+-----------------+-----------------------------------+||内容|+----------------+------------------------------------+都是可能的。综上所述,netty提供了很多优秀的编解码器来适应各种应用协议。你可以更多地使用它们,并找到不同协议之间的差异。本文已收录于http://www.flydean.com/16-netty-buildin-codec-common/最流行的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等你来发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!