Netty核心功能学习

简介: Netty核心功能学习

先看官网一张图:

该图涵盖了Netty的核心功能。


【1】丰富的缓冲实现

Netty 使用自建的 buffer API,而不是使用 NIO 的ByteBuffer 来表示一个连续的字节序列。与 ByteBuffer 相比这种方式拥有明显的优势。


Netty 使用新的 buffer 类型ByteBuf,被设计为一个可从底层解决 ByteBuffer 问题,并可满足日常网络应用开发需要的缓冲类型。这些很酷的特性包括:


如果需要,允许使用自定义的缓冲类型。

复合缓冲类型中内置的透明的零拷贝实现。

开箱即用的动态缓冲类型,具有像StringBuffer 一样的动态缓冲能力。

不再需要调用的flip()方法。

正常情况下具有比 ByteBuffer 更快的响应速度。


Extensibility 可扩展性


ByteBuf 具有丰富的操作集,可以快速的实现协议的优化。


例如,ByteBuf 提供各种操作用于访问无符号值和字符串,以及在缓冲区搜索一定的字节序列。你也可以扩展或包装现有的缓冲类型用来提供方便的访问。


自定义缓冲式仍然实现自 ByteBuf 接口,而不是引入一个不兼容的类型。


【2】Transparent Zero Copy 透明的零拷贝


举一个网络应用到极致的表现,你需要减少内存拷贝操作次数。你可能有一组缓冲区需要被组合以形成一个完整的消息。网络提供了一种复合缓冲,允许你从现有的任意数的缓冲区创建一个新的缓冲区而无需内存拷贝。


例如,一个信息可以由两部分组成:header 和 body。在一个模块化的应用,当消息发送出去时,这两部分可以由不同的模块生产和装配。

+--------+----------+
| header | body |
+--------+----------+

如果你使用的是 ByteBuffer ,你必须要创建一个新的大缓存区用来拷贝这两部分到这个新缓存区中。或者,你可以在 NiO做一个收集写操作,但限制你将复合缓冲类型作为 ByteBuffer 的数组而不是一个单一的缓冲区,打破了抽象,并且引入了复杂的状态管理。此外,如果你不从 NIO channel 读或写,它是没有用的。

// 复合类型与组件类型不兼容。
ByteBuffer[] message = new ByteBuffer[] { header, body };

通过对比, ByteBuf 不会有警告,因为它是完全可扩展并有一个内置的复合缓冲区。

// 复合类型与组件类型是兼容的。
ByteBuf message = Unpooled.wrappedBuffer(header, body);
// 因此,你甚至可以通过混合复合类型与普通缓冲区来创建一个复合类型。
ByteBuf messageWithFooter = Unpooled.wrappedBuffer(message, footer);
// 由于复合类型仍是 ByteBuf,访问其内容很容易,
//并且访问方法的行为就像是访问一个单独的缓冲区,
//即使你想访问的区域是跨多个组件。
//这里的无符号整数读取位于 body 和 footer
messageWithFooter.getUnsignedInt(
messageWithFooter.readableBytes() - footer.readableBytes() - 1);


【3】Automatic Capacity Extension 自动容量扩展


许多协议定义可变长度的消息,这意味着没有办法确定消息的长度,直到你构建消息。或者,在计算长度的精确值时,带来了困难和不便。这就像当你建立一个字符串。你经常估计得到的字符串的长度,让 StringBuffer 扩大了其本身的需求。

// 一种新的动态缓冲区被创建。在内部,实际缓冲区是被“懒”创建,从而避免潜在的浪费内存空间。
ByteBuf b = Unpooled.buffer(4);
// 当第一个执行写尝试,内部指定初始容量 4 的缓冲区被创建
b.writeByte(1);
b.writeByte(2);
b.writeByte(3);
b.writeByte(4);
// 当写入的字节数超过初始容量 4 时,
//内部缓冲区自动分配具有较大的容量
b.writeByte(5);


Better Performance 更好的性能


最频繁使用的缓冲区 ByteBuf 的实现是一个非常薄的字节数组包装器(比如,一个字节)。


与 ByteBuffer 不同,它没有复杂的边界和索引检查补偿,因此对于 JVM 优化缓冲区的访问更加简单。更多复杂的缓冲区实现是用于拆分或者组合缓存,并且比 ByteBuffer 拥有更好的性能。

【4】统一的异步 I/O API


传统的 Java I/O API 在应对不同的传输协议时需要使用不同的类型和方法。例如:java.net.Socket 和 java.net.DatagramSocket 它们并不具有相同的超类型,因此,这就需要使用不同的调用方式执行 socket 操作。


这种模式上的不匹配使得在更换一个网络应用的传输协议时变得繁杂和困难。由于(Java I/O API)缺乏协议间的移植性,当你试图在不修改网络传输层的前提下增加多种协议的支持,这时便会产生问题。并且理论上讲,多种应用层协议可运行在多种传输层协议之上例如TCP/IP,UDP/IP,SCTP和串口通信。


让这种情况变得更糟的是,Java 新的 I/O(NIO)API与原有的阻塞式的I/O(OIO)API 并不兼容,NIO.2(AIO)也是如此。由于所有的API无论是在其设计上还是性能上的特性都与彼此不同,在进入开发阶段,你常常会被迫的选择一种你需要的API。


例如,在用户数较小的时候你可能会选择使用传统的 OIO(Old I/O) API,毕竟与 NIO 相比使用 OIO 将更加容易一些。然而,当你的业务呈指数增长并且服务器需要同时处理成千上万的客户连接时你便会遇到问题。这种情况下你可能会尝试使用 NIO,但是复杂的 NIO Selector 编程接口又会耗费你大量时间并最终会阻碍你的快速开发。


Netty 有一个叫做Channel 的统一的异步 I/O 编程接口,这个编程接口抽象了所有点对点的通信操作。也就是说,如果你的应用是基于 Netty 的某一种传输实现,那么同样的,你的应用也可以运行在 Netty 的另一种传输实现上。


Netty 提供了几种拥有相同编程接口的基本传输实现:


基于 NIO 的 TCP/IP 传输 (见 io.netty.channel.nio),

基于 OIO 的 TCP/IP 传输 (见 io.netty.channel.oio),

基于 OIO 的 UDP/IP 传输, 和本地传输 (见 io.netty.channel.local).

切换不同的传输实现通常只需对代码进行几行的修改调整,例如选择一个不同的ChannelFactory 实现。


此外,你甚至可以利用新的传输实现没有写入的优势,只需替换一些构造器的调用方法即可,例如串口通信。而且由于核心 API 具有高度的可扩展性,你还可以完成自己的传输实现。


【5】基于拦截链模式的事件模型


一个定义良好并具有扩展能力的事件模型是事件驱动开发的必要条件。Netty 具有定义良好的 I/O 事件模型。由于严格的层次结构区分了不同的事件类型,因此 Netty 也允许你在不破坏现有代码的情况下实现自己的事件类型。


这是与其他框架相比另一个不同的地方。很多 NIO 框架没有或者仅有有限的事件模型概念。在你试图添加一个新的事件类型的时候常常需要修改已有的代码,或者根本就不允许你进行这种扩展。


在一个ChannelPipeline 内部一个ChannelEvent 处理。这个管道是Intercepting Filter (拦截过滤器)模式的一种高级形式的实现,因此对于一个事件如何被处理以及管道内部处理器间的交互过程,你都将拥有绝对的控制力。


例如,你可以定义一个从 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);
  }
}


同时你也可以定义一种操作响应其他处理器的写操作请求:

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);
  }
}


有关事件模型的更多信息,请参考 API 文档 ChannelEvent 和ChannelPipeline 部分。


【6】适用快速开发的高级组件


上述所提及的核心组件已经足够实现各种类型的网络应用,除此之外,Netty 也提供了一系列的高级组件来加速你的开发过程。

① Codec 框架


就像“使用POJO代替ChannelBuffer”一节所展示的那样,从业务逻辑代码中分离协议处理部分总是一个很不错的想法。然而如果一切从零开始便会遭遇到实现上的复杂性。你不得不处理分段的消息。一些协议是多层的(例如构建在其他低层协议之上的协议)。一些协议过于复杂以致难以在一台独立状态机上实现。


因此,一个好的网络应用框架应该提供一种可扩展,可重用,可单元测试并且是多层的 codec 框架,为用户提供易维护的 codec 代码。


Netty 提供了一组构建在其核心模块之上的 codec 实现,这些简单的或者高级的 codec 实现帮你解决了大部分在你进行协议处理开发过程会遇到的问题,无论这些协议是简单的还是复杂的,二进制的或是简单文本的。



② SSL / TLS 支持


不同于传统阻塞式的 I/O 实现,在 NIO 模式下支持 SSL 功能是一个艰难的工作。你不能只是简单的包装一下流数据并进行加密或解密工作,你不得不借助于 javax.net.ssl.SSLEngine,SSLEngine 是一个有状态的实现,其复杂性不亚于 SSL 自身。你必须管理所有可能的状态,例如密码套件,密钥协商(或重新协商),证书交换以及认证等。此外,与通常期望情况相反的是 SSLEngine 甚至不是一个绝对的线程安全实现。


在 Netty 内部,SslHandler 封装了所有艰难的细节以及使用 SSLEngine 可 能带来的陷阱。你所做的仅是配置并将该 SslHandler 插入到你的ChannelPipeline 中。同样 Netty 也允许你实现像StartTlS 那样所拥有的高级特性,这很容易。



③ HTTP 实现


HTTP无 疑是互联网上最受欢迎的协议,并且已经有了一些例如 Servlet 容器这样的 HTTP 实现。因此,为什么Netty 还要在其核心模块之上构建一套 HTTP 实现?


与现有的 HTTP 实现相比 Netty 的 HTTP 实现是相当与众不同的。在HTTP 消息的低层交互过程中你将拥有绝对的控制力。这是因为 Netty 的HTTP 实现只是一些 HTTP codec 和 HTTP 消息类的简单组合,这里不存在任何限制——例如那种被迫选择的线程模型。你可以随心所欲的编写那种可以完全按照你期望的工作方式工作的客户端或服务器端代码。这包括线程模型,连接生命期,快编码,以及所有 HTTP 协议允许你做的,所有的一切,你都将拥有绝对的控制力。


由于这种高度可定制化的特性,你可以开发一个非常高效的HTTP服务器,例如:


要求持久化链接以及服务器端推送技术的聊天服务(如,Comet )

需要保持链接直至整个文件下载完成的媒体流服务(如,2小时长的电影)

需要上传大文件并且没有内存压力的文件服务(如,上传1GB文件的请求)

支持大规模混合客户端应用用于连接以万计的第三方异步 web 服务。


④ WebSockets 实现

目录
相关文章
|
5月前
|
开发工具 git
网络编程(三)netty学习demo和笔记和推荐的4本书
网络编程(三)netty学习demo和笔记和推荐的4本书
109 0
|
5月前
|
存储 网络协议 Java
Netty应用实例学习
Netty应用实例学习
21 0
|
5月前
|
编解码 网络协议 Java
Netty基础入门学习
Netty基础入门学习
33 0
|
7月前
|
前端开发 网络协议 API
学习Netty BootStrap的核心知识,成为网络编程高手!
学习Netty BootStrap的核心知识,成为网络编程高手!
49 0
|
12月前
|
Rust Dubbo 网络协议
通过 HTTP/2 协议案例学习 Java & Netty 性能调优:工具、技巧与方法论
通过 HTTP/2 协议案例学习 Java & Netty 性能调优:工具、技巧与方法论
12561 3
|
弹性计算 缓存 网络协议
netty学习(三)
Java NIO 编程
84 0
|
Java
netty学习(二)
Java BIO 编程
95 0
netty学习(二)
|
分布式计算 Dubbo 网络协议
netty学习(一)
netty的介绍和应用场景
135 0
netty学习(一)
|
存储 编解码 安全
基于Netty的IM聊天加密技术学习:一文理清常见的加密概念、术语等
本文正好借此机会,以Netty编写的IM聊天加密为例,为入门者理清什么是PKI体系、什么是SSL、什么是OpenSSL、以及各类证书和它们间的关系等,并在文末附上简短的Netty代码实示例,希望能助你通俗易懂地快速理解这些知识和概念!
187 0
基于Netty的IM聊天加密技术学习:一文理清常见的加密概念、术语等
|
编解码 缓存 Dubbo
Netty流程学习
连接完成之后,不能无所事事,此时应该会执行业务处理。也即此时可以看到上面的NettyServerHandler。因此可以看到dubbo的线程模型: 配置 Dubbo 中的线程模型 如果事件处理的逻辑能迅速完成,并且不会发起新的 IO 请求,比如只是在内存中记个标识,则直接在 IO 线程上处理更快,因为减少了线程池调度。 但如果事件处理逻辑较慢,或者需要发起新的 IO 请求,比如需要查询数据库,则必须派发到线程池,否则 IO 线程阻塞,将导致不能接收其它请求。 如果用 IO 线程处理事件,又在事件处理过程中发起新的 IO 请求,比如在连接事件中发起登录请求,会报“可能引发死锁”异常,但不会真死锁
98 0
Netty流程学习