Netty 架构概述&核心组成

251 阅读8分钟

Netty 架构概述&核心组成

这张图很重要,建议阅读本文时将此图记入脑中。

查看图片请移步:Netty 架构概述&核心组成

传输服务

  1. Socket & Datagram: 套接字,可以简单理解为一种通信接口,为两端通信建立条件,TCP、UDP 就是基于此。
  2. HTTP Tunnel: http 传输隧道
  3. In-VM Piple:java 虚拟机的 piple 通道

协议支持

  1. HTTP & WebSocket 超文本传输协议 & 建立于 TCP 上的全双工通信协议(可用于实现双向通信)
  2. SSL & StartTSL 加密协议
  3. Google Protobuf 谷歌出品的二进制传输协议,轻量高效但不易读。
  4. zip/gzip Compression: 压缩协议
  5. Large File Transfer: 大文件转换
  6. RTSP Real Time Streaming Protocol: 实时流传输协议,多用于多媒体数据传输

核心

  1. Extensible Event Model 可拓展的事件模型
  2. Universal Communication API 统一的通信 API
  3. Zero-Copy-Capable Rich Byte Buffer 零拷贝高性能字节缓冲区

1. Rich Buffer Data Structure 高效缓冲区结构

Netty 使用了自己的 buffer 缓冲区代替了 NIO 实现的 ByteBuffer 来表示字节序列,与使用 ByteBuffer 相比这种方法具有显著的优势。

Netty 从头开始设计了 ChannelBuffer ,其解决了 ByteBuffer 的一些问题,同时满足日常开发所需要的功能。

这里引申出两个问题:

  1. 使用 ChannelBuffer 相比 ByteBuffer 有哪些优势?
  2. ChannelBuffer 解决了 ByteBuffer 的什么问题?即 ByteBuffer 存在怎样的问题?

带着这两个问题我们来看看 ChannelBuffer 具有的特性

特性:

  • 如果必要的话,你可以定义自己的 buffer 类型。
  • 通俗易懂的零拷贝已被内置的复合缓冲区类型所实现。
  • 动态缓冲区类型是开箱即用的,其容量可以根据需求动态拓展,就像 StringBuffer 一样。
  • 不再需要调用 flip() 函数了
  • 其性能通常快于 ByteBuffer
合并和切片 ChannelBuffers

combine-slice-buffer.png

在通信层进行数据传输时,通常需要对数据进行组合或者切片。例如 将如果一个有效载荷被拆分为多个数据包,这时就需要我们将其合并后再进行解码。

传统的解决方案,要将多个数据包的数据复制到一个新的缓冲区来进行组合。

Netty 支持零复制的方法,即使用 ChannelBuffers ,其中使用 ChannelBuffer 来指向所需要的缓冲区从而避免了拷贝。(还有一个零拷贝所说的是 避免数据在 内核空间 到 用户空间的拷贝,其也是通过引用来实现)

2.通用异步 I/O API

传统的 Java I/O API 为不同的传输方式提供了不同的方法,这样的差异意味着不统一

例如 java.net.socketjava.net.DatagramSocket 他们没有一个共同的抽象超类,且在处理 Socket I/O 上面他们完全采用不同的方式方法。

这种差异,或者说不统一使得基于此实现的网络应用程序在想要从一种传输方式转换到另一种传输方式的时候是困难且枯燥的。这意味着我们可能面临着处理同一套逻辑使用不同的方法。

通常来说,网络协议可以在不只一种传输上运行,如 TCP/IP 、UDP/IP 、SCTP、串行端口通信,当我们需要支持多种传输时,这种不可移植性会成为一个巨大的问题,这意味着我们需要重写应用程序的网络层逻辑。

更糟糕的是,Java 的 NIO 在设计上并不兼容 OIO 并且在 AIO 中也不进行兼容,由于这些 API 在设计和特性上都不尽相同,这意味着在程序设计之初,就必须要决定你的应用要依赖于那个 API ,一旦确定很难更改。

Netty 为此提供了统一的异步 I/O 接口 Channel

其抽象了所有点对点通信所需要的方法,这意味着你只要在一种 Netty 的传输上编写应用程序,你的应用程序就可以运行在其他类型的传输类型上。

Netty 通过一个通用的接口提供了几种基本的传输方式:

  • NIO 基于 TCP/IP 的传输方式 org.jboss.netty.channel.socket.nio
  • OIO 基于 TCP/IP 的传输方式 org.jboss.netty.channel.socket.oio
  • OIO 基于 UDP/IP 的传输方式
  • 本地传输方式 org.jboss.netty.channel.local

使用 Netty 转换传输方式通常只需要修改几行代码即可,如更改选择不同的 ChannelFactory 实现

3. 基于责任链模式的事件模型

对于事件驱动的应用程序来说,一个良好定义可拓展的事件模型是必不可少的。Netty 有一个明确的专注于 I/O 的事件模型。其允许在不破坏现有代码结构的前提下实现自己的事件类型。

ChannelEvent 通过 ChannelPipline 中的 ChannelHandler 列表进行处理。ChannelPipline 实现了高级的责任链模式,使得用户可以完全控制 事件如何被处理,以及各个 处理器在 ChannelPipline 之间的作用关系。

例如你可以定义 当数据从 socket 中读取过来时如何去做。

public class MyReadHandler implements SimpleChannelHandler {
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent evt) {
            Object message = evt.getMessage();
         // Do something with the received message.
            ...

        // And forward the event to the next handler.
        ctx.sendUpstream(evt);
      }
 }

同样你也可以定义当 handler 接收到写入请求时的处理逻辑

public class MyWriteHandler implements SimpleChannelHandler {
       public void writeRequested(ChannelHandlerContext ctx, MessageEvent evt) {
            Object message = evt.getMessage();
           // Do something with the message to be written.
            ...
   
            // And forward the event to the next handler.
           ctx.sendDownstream(evt);
        }
  }

4. 提供了快速开发的高级组件

除了上述的核心组件,Netty 同时提供了高级组件以便进行快速开发。

编解码框架:

Netty 提供了许多基础和高级的编解码器,来解决我们自己编写编解码器所遇到的大多数问题,如:处理消息碎片,以及一些多层的协议的处理(即建立在其他较低级别的协议之上)。

SSL/TLS 支持:

在 NIO 中支持 SSL 并不是一件简单的事情。你不能简单的将一个流包装起来进行加密或者解密,你必须使用 javax.net.ssl.SSLEngine SSLEngine 是一个与 SSL 本身同样复杂的状态机。你必须管理所有可能的状态,例如密码套件、加密密钥协商、重新协商、证书交换和验证等,此外 SSLEngine 不是完全线程安全的。

Netty 为此提供了 SslHandler 。SslHandler 处理了 SSLEngine,我们需要做的就是配置 SslHandler 并将其配置到 ChannelPipline 中。同时其支持轻松实现 StartTLS

HTTP 实现:

HTTP 作为目前网络最流行的协议 Netty 提供了对其的实现,目前已有许多对 HTTP 的实现如 Servlet 容器,而 Netty 对 HTTP 的支持与现有的库非常不同,其支持控制 HTTP 消息在低级别下的消息交换。因为它基本上是 HTTP 编解码器 和 HTTP 消息类的组合,所以没有强制线程模型的限制。

所以我们可以用 Netty 编写 HTTP 服务器以任何我们想要的方式,可以完全控制 HTTP 规范中的所有内容,包括线程模型、连接生命周期和分块编码

由于其高度可定制的特性,可以编写一个非常高效的 HTTP 服务器,例如:

  • 需要持久连接和服务器推送技术的聊天服务器(例如 Comet 基于 HTTP 长连接的“服务器推”技术)
  • 需要保持连接打开的媒体流服务器,直到整个媒体被流式传输(例如 2 小时的视频)
  • 允许上传大文件而没有内存压力的文件服务器(例如每个请求上传 1GB)
  • 可扩展的混搭客户端,可异步连接到数以万计的 3rd 方 Web 服务

Webscoket 实现:

Webscoket 允许通过单个传输控制协议 (TCP) 套接字实现双向、全双工通信通道。它旨在允许在 Web 浏览器和 Web 服务器之间传输数据。Netty 提供了对其的实现io.netty.handler.codec.http.websocketx

集成 Google Protocol Buffer:

想要实现高性能的二进制协议客户端服务器 Google 的 Protocol Buffer 绝对是不二之选,Netty 为我们提供了ProtobufEncoder 和 ProtobufDecoder。你可以将通过 Google Protocol Buffer 生成的消息类使用 Netty 进行编解码。

总结

Netty 为我们提供了:

高效字节缓冲区,零拷贝技术。

统一的 异步 I/O 通信 API,快速切换传输方式。

基于责任链模式的事件模型 ,无侵入定制自己的事件处理,以及控制各个处理器之间的关系。

快速开发高级组件,开箱即用的高级组件,为快速开发定制提供了可能。

Netty 最核心的三个组件Buffer 缓冲区、Channel 通道 、Event model 事件模型。其余的高级功能都是基于这三个核心组件构建的。

所以,如果你想自己构建通信服务器,Netty 绝对是一个很好的选择,不论是 HTTP 、Wetscoket、Mqtt 等你都可以自己定制。

欢迎关注我的公众号,我是 dying 搁浅