netty系列之:netty中的懒人编码解码器
文章目錄
- 簡(jiǎn)介
- netty中的內(nèi)置編碼器
- 使用codec要注意的問題
- netty內(nèi)置的基本codec
- base64
- bytes
- compression
- json
- marshalling
- protobuf
- serialization
- string
- xml
- 總結(jié)
簡(jiǎn)介
netty之所以強(qiáng)大,是因?yàn)樗鼉?nèi)置了很多非常有用的編碼解碼器,通過使用這些編碼解碼器可以很方便的搭建出非常強(qiáng)大的應(yīng)用程序,今天給大家講講netty中最基本的內(nèi)置編碼解碼器。
netty中的內(nèi)置編碼器
在對(duì)netty的包進(jìn)行引入的時(shí)候,我們可以看到netty有很多以netty-codec開頭的artifactId,統(tǒng)計(jì)一下,有這么多個(gè):
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個(gè)codec包,其中netty-codec是最基礎(chǔ)的一個(gè),其他的9個(gè)是對(duì)不同的協(xié)議包進(jìn)行的擴(kuò)展和適配,可以看到netty支持常用的和流行的協(xié)議格式,非常的強(qiáng)大。因?yàn)閏odec的內(nèi)容非常多,要講解他們也不是很容易,本文將會(huì)以netty-codec做一個(gè)例子,講解其中最基本的也是最通用的編碼解碼器。
使用codec要注意的問題
雖然netty提供了很方便的codec編碼解碼器,但是正如我們?cè)谇耙黄恼轮刑岬降?#xff0c;有些codec是需要和Frame detection一起配合使用的,先使用Frame detection將ByteBuf拆分成一個(gè)個(gè)代表真實(shí)數(shù)據(jù)的ByteBuf,再交由netty內(nèi)置的codec或者自定義的codec進(jìn)行處理,這樣才能起到應(yīng)有的效果。
netty內(nèi)置的基本codec
netty中基本的codec有base64、bytes、compression、json、marshalling、protobuf、serialization、string和xml這幾種。
下面將會(huì)一一進(jìn)行講解。
base64
這個(gè)codec是負(fù)責(zé)ByteBuf和base64過后的ByteBuf之間的轉(zhuǎn)換。雖然都是從ByteBuf到ByteBuf,但是其中的內(nèi)容發(fā)生了變化。
有兩個(gè)關(guān)鍵的類,分別是Base64Encoder和Base64Decoder。因?yàn)锽ase64Decoder是一個(gè)MessageToMessageDecoder,所以需要使用一個(gè)DelimiterBasedFrameDecoder提前進(jìn)行處理,常用的例子如下:
ChannelPipeline pipeline = ...;// Decoderspipeline.addLast("frameDecoder", new DelimiterBasedFrameDecoder(80, Delimiters.nulDelimiter()));pipeline.addLast("base64Decoder", new Base64Decoder());// Encoderpipeline.addLast("base64Encoder", new Base64Encoder());bytes
bytes是將bytes數(shù)組和ByteBuf之間進(jìn)行轉(zhuǎn)換,同樣的在decode之前,也需要使用FrameDecoder,通常的使用方式如下:
ChannelPipeline pipeline = ...;// Decoderspipeline.addLast("frameDecoder",new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));pipeline.addLast("bytesDecoder",new ByteArrayDecoder());// Encoderpipeline.addLast("frameEncoder", new LengthFieldPrepender(4));pipeline.addLast("bytesEncoder", new ByteArrayEncoder());compression
compression這個(gè)包的內(nèi)容就比較豐富了,主要是對(duì)數(shù)據(jù)的壓縮和解壓縮服務(wù)。其支持的算法如下:
brotli Bzip2 FastLZ JdkZlib Lz4 Lzf Snappy Zlib Zstandardcompression對(duì)于大數(shù)據(jù)量的傳輸特別有幫助,通過壓縮可以節(jié)省傳輸?shù)臄?shù)據(jù)量,從而提高傳輸速度。
但是壓縮是使用特定的算法來計(jì)算的,所以它是一個(gè)高CPU的操作,我們?cè)谑褂玫臅r(shí)候需要兼顧網(wǎng)絡(luò)速度和CPU性能,并從中得到平衡。
json
json這個(gè)包里面只有一個(gè)JsonObjectDecoder類,主要負(fù)責(zé)將Byte流的JSON對(duì)象或者數(shù)組轉(zhuǎn)換成JSON對(duì)象和數(shù)組。
JsonObjectDecoder直接就是一個(gè)ByteToMessageDecoder的子類,所以它不需要FrameDecoder,它是根據(jù)括號(hào)的匹配來判斷Byte數(shù)組的起始位置,從而區(qū)分哪些Byte數(shù)據(jù)是屬于同一個(gè)Json對(duì)象或者數(shù)組。
我們?nèi)绻M褂肑SON來傳輸數(shù)據(jù)的話,這個(gè)類就非常有用了。
marshalling
Marshalling的全稱叫做JBoss Marshalling,它是JBoss出品的一個(gè)對(duì)象序列化的方式,但是JBoss Marshalling的最新API還是在2011-04-27,已經(jīng)有10年沒更新了,是不是已經(jīng)被廢棄了?
所以這里我們不詳細(xì)介紹這個(gè)序列化的內(nèi)容,感興趣的小伙伴可以自行探索。
protobuf
protobuf大家應(yīng)該都很熟悉了,它是google出品的一種信息交換格式,可以將其看做是一種序列化的方式。它是語(yǔ)言中立、平臺(tái)中立、可擴(kuò)展的結(jié)構(gòu)化數(shù)據(jù)序列化機(jī)制,和XML類似,但是比XML更小、更快、更簡(jiǎn)單。
netty對(duì)protobuf的支持在于可以將protobuf中的message和MessageLite對(duì)象跟ByteBuf進(jìn)行轉(zhuǎn)換。
protobuf的兩個(gè)編碼器也是message到message直接的轉(zhuǎn)換,所以也需要使用frame detection。當(dāng)然你也可以使用其他的frame detection比如LengthFieldPrepender和LengthFieldBasedFrameDecoder如下所示:
ChannelPipeline pipeline = ...;// Decoderspipeline.addLast("frameDecoder",new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));pipeline.addLast("protobufDecoder",new ProtobufDecoder(MyMessage.getDefaultInstance()));// Encoderpipeline.addLast("frameEncoder", new LengthFieldPrepender(4));pipeline.addLast("protobufEncoder", new ProtobufEncoder());其中LengthFieldPrepender會(huì)自動(dòng)給字段前面加上一個(gè)長(zhǎng)度字段:
之前:+----------------+| "HELLO, WORLD" |+----------------+之后:+--------+----------------++ 0x000C | "HELLO, WORLD" |+--------+----------------+當(dāng)然netty為protobuf準(zhǔn)備了兩個(gè)專門的frame detection,他們是ProtobufVarint32FrameDecoder和ProtobufVarint32LengthFieldPrepender。在講解這兩個(gè)類之前,我們需要了解一下protobuf中的Base 128 Varints。
什么叫Varints呢?就是序列化整數(shù)的時(shí)候,占用的空間大小是不一樣的,小的整數(shù)占用的空間小,大的整數(shù)占用的空間大,這樣不用固定一個(gè)具體的長(zhǎng)度,可以減少數(shù)據(jù)的長(zhǎng)度,但是會(huì)帶來解析的復(fù)雜度。
那么怎么知道這個(gè)數(shù)據(jù)到底需要幾個(gè)byte呢?在protobuf中,每個(gè)byte的最高位是一個(gè)判斷位,如果這個(gè)位被置位1,則表示后面一個(gè)byte和該byte是一起的,表示同一個(gè)數(shù),如果這個(gè)位被置位0,則表示后面一個(gè)byte和該byte沒有關(guān)系,數(shù)據(jù)到這個(gè)byte就結(jié)束了。
舉個(gè)例子,一個(gè)byte是8位,如果表示的是整數(shù)1,那么可以用下面的byte來表示:
0000 0001如果一個(gè)byte裝不下的整數(shù),那么就需要使用多個(gè)byte來進(jìn)行連接操作,比如下面的數(shù)據(jù)表示的是300:
1010 1100 0000 0010為什么是300呢?首先看第一個(gè)byte,它的首位是1,表示后面還有一個(gè)byte。再看第二個(gè)byte,它的首位是0,表示到此就結(jié)束了。我們把判斷位去掉,變成下面的數(shù)字:
010 1100 000 0010這時(shí)候還不能計(jì)算數(shù)據(jù)的值,因?yàn)樵趐rotobuf中,byte的位數(shù)是反過來的,所以我們需要把上面的兩個(gè)byte交換一下位置:
000 0010 010 1100也就是:
10 010 1100=256 + 32 + 8 + 4 = 300
在protobuf中一般使用Varint作為字段的長(zhǎng)度位,所以netty提供了ProtobufVarint32LengthFieldPrepender和ProtobufVarint32FrameDecoder對(duì)ByteBuf進(jìn)行轉(zhuǎn)換。
比如為ByteBuf添加varint的length:
BEFORE ENCODE (300 bytes) AFTER ENCODE (302 bytes)+---------------+ +--------+---------------+| Protobuf Data |-------------->| Length | Protobuf Data || (300 bytes) | | 0xAC02 | (300 bytes) |+---------------+ +--------+---------------+解碼的時(shí)候刪除varint的length字段:
BEFORE DECODE (302 bytes) AFTER DECODE (300 bytes)+--------+---------------+ +---------------+| Length | Protobuf Data |----->| Protobuf Data || 0xAC02 | (300 bytes) | | (300 bytes) |+--------+---------------+ +---------------+serialization
序列化就是把對(duì)象轉(zhuǎn)換成二進(jìn)制數(shù)據(jù),事實(shí)上所有的codec都可以成為序列化。他們提供了對(duì)象和byte之間的轉(zhuǎn)換方法。
netty也提供了兩個(gè)對(duì)象的轉(zhuǎn)換方法:ObjectDecoder和ObjectEncoder。
要注意的是,這兩個(gè)對(duì)象和JDK自帶的ObjectInputStream和ObjectOutputStream,是不兼容的,如果要兼容,可以使用CompactObjectInputStream、CompactObjectOutputStream和CompatibleObjectEncoder。
string
String是我們最常使用到的對(duì)象,netty為string提供了StringDecoder和StringEncoder。
同樣的,在使用這兩個(gè)類之前,需要將消息進(jìn)行轉(zhuǎn)換,通常使用的是 LineBasedFrameDecoder按行進(jìn)行轉(zhuǎn)換:
ChannelPipeline pipeline = ...;// Decoderspipeline.addLast("frameDecoder", new LineBasedFrameDecoder(80));pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));// Encoderpipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));xml
xml也是一個(gè)非常常用的格式,但是它的體積會(huì)比較大,現(xiàn)在應(yīng)該用的比較少了。netty提供了一個(gè)XmlFrameDecoder來進(jìn)行解析。
因?yàn)閤ml有自己的開始和結(jié)束符,所以不需要再做frame detection,直接轉(zhuǎn)換即可,如:
+-----+-----+-----------+| <an | Xml | Element/> |+-----+-----+-----------+ 轉(zhuǎn)換成:+-----------------+| <anXmlElement/> |+-----------------+ +-----+-----+-----------+-----+----------------------------------+| <an | Xml | Element/> | <ro | ot><child>content</child></root> |+-----+-----+-----------+-----+----------------------------------+轉(zhuǎn)換成:+-----------------+-------------------------------------+| <anXmlElement/> | <root><child>content</child></root> |+-----------------+-------------------------------------+都是可以的。
總結(jié)
netty提供了很多優(yōu)秀的codec來適配各種應(yīng)用協(xié)議,大家可以多用用,找找不同協(xié)議的不同之處。
本文已收錄于 http://www.flydean.com/16-netty-buildin-codec-common/
最通俗的解讀,最深刻的干貨,最簡(jiǎn)潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!
歡迎關(guān)注我的公眾號(hào):「程序那些事」,懂技術(shù),更懂你!
總結(jié)
以上是生活随笔為你收集整理的netty系列之:netty中的懒人编码解码器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: netty系列之:内置的Frame de
- 下一篇: 在java程序中使用protobuf