Chapter2 Netty的架构概览
![](https://img.haomeiwen.com/i13084796/91cb8708ce4a2c2e.png)
在这章中,我们将检查一下Netty都提供了哪些核心功能,以及依赖于这些核心的功能是如何开发出一个网络应用的。在读这章的内容时,请把这个图牢记于心。同时如果你要了解更多的细节,可以查看javadoc。
一、Rich Buffer 数据结构
Netty没有使用NIO的ByteBuffer,而是使用的是它自己的buffer API 来表示一系列的字节。相比于使用ByteBuffer,这种方式能带来巨大的优势。Netty的新buffer类型ChannelBuffer是设计用来解决ByteBuffer的问题,同时也是迎合网络应用开发人员的日益需要。
这里列举一下它的几个炫酷特性:
-
在必要时,你可以定义自己的buffer类型
-
使用复合的buffer类型,可以实现透明零拷贝
-
提供开箱即用的动态buffer类型,随着要求的越来越多,它的功能也被极大的扩展,就向StringBuffer一样。
-
不需要在调用flip()
-
比ByteBuffer更快
更多信息可以查看地址
1.1 ChannelBuffer的组合与切分
当数据在传输层间流动时,都经常面临着被重组和分片。举例来说,如果一个payload被切分成多个包,那么通常在解码时,需要把它们重新组合起来。
传统地,把来自多个包中的数据组合到一起,我们都是通过把它们拷贝进一个新的字节buffer里面来实现的。Netty有一个支持零拷贝的方式zero-copy,在这个方式下,它会把ChannelBuffer指向需要的缓存,从而消除了执行拷贝的操作。
二、统一异步I/O API
java的传统I/O API会针对不同的传输类型提供不同的类和方法。例如, java.net.Socket 和 java.net.DatagramSocket就没有相同的父级类,也因此他们执行套接字I/O的方式也十分不同。
正是这种搭配不当,使得在移植一个网络应用时,十分繁琐困难。当你需要支持额外的传输协议时,这种可移植性的匮乏就成了问题。
因为我们经常需要重写引用的网络层。理论上,许多协议都是可以运行在多个传输协议上的,比如: TCP/IP ,UDP/IP ,SCTP 和串口通信。
Netty有一个统一的异步IO接口Channel, Channel把点对点通信的所有操作都抽取了出来。也就是说,你在某个Netty传输层上写的应用可以在另一个N
etty传输层上运行。Netty借助于一个统一的API提供了许多必需的传输层:
-
NIO-based TCP/IP transport (See org.jboss.netty.channel.socket.nio),
-
OIO-based UDP/IP transport, and
-
Local transport (See org.jboss.netty.channel.local).
从一个传输协议切换到另一个传输协议通常只需要改动几行代码,比如:选择一个不同的ChannelFactory实现。
三、基于拦截器链模式的事件模型
对于事件驱动应用来说,一个定义良好、可扩展的事件模型是必需的。针对IO操作,Netty有一个定义良好的事件驱动。它使得你可以在不破坏现存代码的情况下,实现自己的事件类型。因为每个事件类型都通过严格的类型层次而彼此不同。这和其他的框架有点区别。许多NIO框架没有或者只是具有有限的事件模型的概念。这样造成的结果就是,当你添加自定义的事件类型时,他们如果提供扩展的话,他们就不得不破坏现在代码。
每个ChannelEvent都会交由ChannelPipeline中的一系列ChannelHandler来处理。pipeline所实现的就是一种更高级别的拦截器过滤器模式,这种模式使得用户对事件处理流程,pipeline中的handler之间交互实现全部控制。例如,当数据从套接字中读到之后,你可以定义要执行什么操作。
1 public class MyReadHandler implements SimpleChannelHandler {
2 public void messageReceived(ChannelHandlerContext ctx, MessageEvent evt) {
Object message = evt.getMessage();
4 // Do something with the received message.
...
6
// And forward the event to the next handler.
8 ctx.sendUpstream(evt);
}
10 }
当一个handler收到写请求之后,你也能定义执行什么动作:
1 public class MyWriteHandler implements SimpleChannelHandler {
2 public void writeRequested(ChannelHandlerContext ctx, MessageEvent evt) {
Object message = evt.getMessage();
4 // Do something with the message to be written.
...
6
// And forward the event to the next handler.
8 ctx.sendDownstream(evt);
}
10 }
获取更多关于event model的信息,可以查阅 ChannelEvent和ChannelPipeline的文档。
四、针对快速开发的高级组件
基于上面提及的核心组件,已经能够开放出所有类型的网络应用了。同时,Netty也提供了一组更高级的特性以加速开发工作。
4.1 Codec框架
正如在第八节中提到的那样,把编解码工作从业务逻辑中抽取出来总是十分有利的。但是,在具体实现编解码工作时,又有些复杂。
不得不处理消息的拆包和粘包问题。总而言之,一个好的网络应用框架应该用户提供: 可扩展、可复用、可单测、多层次编解码的特性。
Netty 提供了许多基本和高级的编解码特性,来解决这些问题。当你要实现一个复杂或简单的协议时,你将用到他们-----一句话,很简单。
4.2 SSL /TLS 支持
和阻塞式IO不同的是,在NIO里面支持SSL是很麻烦的。你不能简单地把数据流加解密成密文或明文数据,你要使用javax.net.ssl.SSLEngine。SSLEngine是个状态机,它就和SSL一样复杂。你不得不管理所有的状态,
密码匹配、秘钥协商,证书交换、校验。此外,SSLEngine并不是线程安全的。
在Netty里面,SslHandler会帮助你处理好所有这些残酷的细节以及SSLEngine的缺陷。你所需要做的就是配置SslHandler,然后把其插入到你的ChannelPipeline中。同时,你还可以很轻松地实现像StartTLS那样的高级特性。
4.3 HTTP实现
Http肯定是互联网上最受欢迎的协议。有许多http协议实现,比如: Servlet容器,那么,Netty为何要在其核心之上支持HTTP协议呢?Netty的HTTP实现不同于现存的HTTP库,它可以让你在底层实现对HTTP消息的完全控制。因为它基本上就是HTTP codec 和Http消息类的组合。并没有像严格的线程模型这样的限制。即: 你可以按照你的需要,实现一个你自己的HTTP client和server端。你可以控制HTTP协议中的一切。包括:线程模型、连接生命周期、数据块的编解码。
正是这些高可定制化的特性,你可以写一个高效的HTTP服务器,例如:
- 聊天服务器,这需要用到连接持久化和服务端推送技术
- 流媒体服务器, 这需要在流结束之前,一直保持连接的打开
- 文件服务器,能够上传大文件而没有任何内存压力(e.g: 每次请求上传 1GB )
- 大规模的混合的客户端,它能够异步地连接到上万个第三方web服务上。
4.4 WebSocket实现
WebSocket支持双向、全双工的通信通道。它可以让web浏览器和web服务器实现数据流的交互。WebSocket协议已经被IETF作为RFC 6455实现了标准化。
4.5 整合Google Protocol Buffer
Google Protocol Buffer 是针对快速实现一个高性能二进制协议的完美解决方案。通过ProtobufEncoder和ProtobufDecoder,你可以把Google Protobuf 生成的消息类转换成Netty 编解码器。请查看"LocalTime"案例,这里面告诉你如何创建一个高性能的二进制协议的客户端和服务端。
五、总结
在此章节中,我们概括地讲述了Netty的所有特性。Netty具有一个简单,强大的架构。它主要由三个组件构成: buffer 、channel、 event model 。所有的高级特性都是建立在这三个核心组件的基础之上。一旦你理解了这三个组件的工作原理,你也就不难理解更高级的特性。
网友评论