11.4 Decoding delimited and length-based protocols
如果你使用Netty工作的时候,你经常会遇到使用分隔符或者指定字节数的方法来解码,接下来的一个小节中将讲解Netty在处理这种问题给出的一些具体实现
11.4.1 Delimited protocols
分隔符协议使用某个固定的字符来标记一个信息或者一个信息端的开头和结尾,被分割的消息块我们称之为“帧”,由RFC文档定义的有很多正式的规范协议,例如SMTP,POP3,IMAP,Telnet,当然,很多私有组织经常也会有他们专有的格式,不管你是使用哪种协议进行工作的,表格11.5列出的一些解码器将会帮助你自定义一些解码器来帮助你可以提取任何分隔符切割的帧数据
图11.5展示了一些帧数据以\r\n为结尾切分的数据如何被分割处理的
下面的代码清单展示了如何使用LineBasedFrameDecoder来实现图11.5展示的问题
如果你使用的帧数据是以某种分隔符切分的,而不是以行为结尾分割切分的话,你可以使用DelimiterBasedFrameDecoder来简化你对这种场景下帧数据的处理,只需要在构造器中指定具体的分割符就可以了
这些解码器是你实现自定义解码器的工具,我们举例说明,我们可以使用如下的场景说明:
1)输入的数据流包含多个数据帧,每一个数据帧以\n为结尾
2)每一个数据帧中包含多个项目,每一个项目将以一个空格符切分
3)每一个帧代表一个命令,这个命令又是有命令的名称和多个参数组成的
我们对于上述的协议规定给出的自定义的解码器簇中给出如下的几个类:
1)Cmd--------在一个ByteBuf中存储一个帧或者一个命令的内容,包括存储该命令的名称和参数
2)CmdDecoder-----------通过覆盖decode方法来检索一行数据,从这行数据中构造一个或者多个Cmd的实例
3)CmdHandler------------从CmdDecoder中获取Cmd对象,并对此对象做一些操作
4)CmdHandlerInitializer----------------为了方便,我们将先前定义的一些类通过专门的ChannelInitializer嵌入到管道的handler链中去
完整的代码清单如11.9所示,这个解码器的核心是实现了LineBasedFrameDecoder
11.4.2 Length-based protocols
一个基于长度的规范定义了一个帧数据在这个帧数据的头部编译了这个帧数据的长度,而不是用某些指定的字符去切分字符,表11.6展示了Netty提供的2中解码器来处理这种类型的规范协议
图11.6展示了固定帧大小的FixedLengthFrameDecoder的解码器如何处理8字节的帧数据的
你经常遇到一种场景,帧数据的大小并不是一个固定的,为了处理这种数据帧的大小是变量的问题我们可以使用LengthFieldBasedFrameDecoder,这个类可以从每个帧数据的头部信息知道帧数据的大小,然后从数据流中获取对应大小的帧数据
图11.7展示了一个例子,这个例子中帧数据头部的长度占用了2个字节,下标从0开始,长度有2个字节
LengthFieldBasedFrameDecoder提供了多个构造器来覆盖帧数据头部数据多变的场景,代码清单11.10使用了一个构造器,这个构造器的三个入参是maxFrameLength,lengthFieldOffset和LengthFieldLength,在这个使用案例中,每个帧的长度是在帧的前八个字节中定义的
截止到目前为止,你已经学习过Netty提供的几个译码器,这几个译码器用来支持多种数据帧的结构,这些数据帧的结构规范包括如下几种,一是以某种字符分割切分而形成的数据帧,而是固定长度的数据帧,三是变长的数据帧,该数据帧的长度会在该帧的头部指定,随着更多协议和规范的形成然你会发现和编写更多各种各样的译码器,并且将这些译码器进行归类和分档,形成一个更加完整的体系
11.5 Writing big data
如何在异步框架中高效地写入大数据块会因为网络饱和的可能性成为一个很特别的问题,因为写的操作是不会阻塞的,他们甚至会在所有的数据并没有完全写入的时候返回完成状态并且通知ChannelFuture,当这种情况发生的时候,你将会冒着内存溢出的风险不停的写入且写入的操作无法被终止,所以当你写入大数据块的时候,你需要做好准备处理这样的案例:当慢速连接到一个远程端的时候会产生释放内存的延迟,我们举一个例子写入一个文件到网络中
在我们之前4.2章节讨论的传输协议上我们提及过NIO的零拷贝的特性,这个特性会消除拷贝过程,当需要将文件系统中的一个文件移动网络中的时候,这些过程将会在Netty的核心模块发生,所以在做这个实践的时候,应用需要给出接口FileRegion的一个具体实现,这个接口在Netty的文档中是这样定义的:某个文件的区域需要通过Channel发送这块区域需要支持零拷贝传输
下面的代码清单中展示了你如何在使用零拷贝的技术传输一个文件内容的,从FileInputStream中创建一个DefaultFileRegion,且将其写入到Channel中
这个例子中只能应用直接的文件内容传输,系统不能有对该文件的任何操作,如果有对该文件的操作需要你把数据从文件系统中拷贝到用户内存里来,你可以使用ChunkedWriteHandler,它支持在没有高内存损耗的情况下异步地写入大的数据流
做到这个功能的核心就是接口ChunkedInput<B>,这里的B指的是方法readChunk方法返回的类型,这个接口给出了四个实现,如下列表11.7所示,每一个都代表了被ChunkedWriteHandler消费的一个没有限定长度的数据流
代码清单11.12说明了ChunkedStream的使用,这种类型的实现经常使用在实际场景中,这个类也展示了文件和SslContext的初始化,当initChannel方法调用的时候,它初始化了包涵链式处理器的Channel
当channel的状态被激活的时候,WriteStreamHandler将会把文件块当做一个ChunkedStream写入,在文件传输之前会使用SslHandler进行加密
块状输入:使用你自己ChunkedInput的实现在管道中安装一个ChunkedWriteHandler
在这个小节中,我们讲解了如何使用零拷贝这种特性来高效地传输,如何不冒着OOM的风险传输大数据------使用ChunkedWriteHandler,在下一个小节中,我们将介绍几种序列化POJOs的几种方式
11.6 Serializing data
JDK提供了ObjectOutputStream和ObjectInputStream用来序列化和反序列化原生的数据类型和POJO用来网络传输,这系列的API并不是很复杂,可以应用到任何实现java.io.Serializable接口的对象,但是这并不是非常高效的方法,在这个小节中,我们将了解Netty对序列化提供的一些工具类
11.6.1 JDK serialization
如果你的应用使用ObjectOutputStream和ObjectInputStream来与远程端进行交互的时候,兼容性的问题是你最主要担心的问题,JDK的序列化将是你解决这类问题的最好方法,表11.8展示了Netty提供的一些基于JDK的一些序列化的类的介绍
11.6.2 Serialization
with JBoss Marshalling
如果你可以自由选择使用一些其他的依赖,那么JBoss的Marshalling将是一个不错的选择,它将比JDK的序列化的速度快3倍且序列化的内容显得更加紧凑
Netty提供了对JBoss的Marshalling的支持,以解码器和编码器的形式支持,如表11.9所示,第一块是以JDK的序列化的实现做到了兼容性的支持,第二种是在保证兼容性的同时,提供了最大的性能,它底层是使用JBoss的Marshalling实现的
下面的代码清单再次向你展示了如何使用MarshallingDecoder和MarshallingEncoder,这几乎只需要正确的配置ChannelPipeline就可以了
、
11.6.3
Serialization via Protocol Buffers
Netty对序列化的最后一个解决方案是使用Protocol Buffers的一个译码器,这个ProtoBuf是由Google开发用户进行数据交互的,现在已经开源了,源代码可以在https://github.com/google/protobuf网站上找到
ProtoBuf对结构化的数据以更加紧凑更加高效地方式进行编码和解码,它现在已经被多种语言进行了实现,使其能够进行更好地应用于跨语言的项目,表11.10展示了Netty对ProtoBuf支持而提供的ChannelHandler的实现
在这里,我们再一次只需要将恰当的ChannelHandler增加到ChannelPipeline中去,如下面代码清单11.14所示:
在这个小节中,我们介绍了Netty提供的几种序列化方式,这几种序列化方式是通过编码和解码的形式给出的,且这几种序列化方法是基于如下几种具体的实现的1)JDK的序列化方式,JBoss的Marshalling和Google的ProtoBuf
11.7 Summary
由Netty原生提供的几个译码器和处理器可以联合使用,用来处理各式各样的业务场景,并且这些组件已经在很多大型的业务项目的使用过程中被证明是稳健的
请注意,在我们本章节的介绍过程中,我们只是对非常常用的API给出了最常用的使用方式,更多详细且精细的API请参考API文档
在下一个章节中,我们将学习另一个比较先进的协议用来提高web开发的性能的协议WebSocket,Netty对WebSocket提供了很多工具,利用这些工具你可以很方便地使用WebSocket