Netty学习笔记(四)传输
一、传输APi
流经网络的数据总是具有相同的类型:字节。这些字节是如何流动的主要取决于我们所说的 网络传输— 一个帮助我们抽象底层数据传输机制的概念。用户并不关心这些细节,他们只想确保他们的字节被可靠地发送和接收。Netty 为它所有的传输实现提供了一个通用 API——Channel,它被用于所有IO操作,通过它可以很方便的实现传输逻辑。下图是Channel的类层次图。

如图所示,每个 Channel 都将会被分配一个 ChannelPipeline 和 ChannelConfig。 ChannelConfig 包含了该 Channel 的所有配置设置,并且支持热更新。由于特定的传输可能具有独特的设置,所以它可能会实现一个 ChannelConfig 的子类型。由于 Channel 是独一无二的,所以为了保证顺序将 Channel 声明为 java.lang.Comparable 的一个子接口。因此,如果两个不同的 Channel 实例都返回了相同的散列码,那么 AbstractChannel 中的compareTo()方法的实现将会抛出一个 Error。ChannelPipeline 持有所有将应用于入站和出站数据以及事件的 ChannelHandler 实例,这些 ChannelHandler 实现了应用程序用于处理状态变化以及数据处理的逻辑。ChannelHandler 的典型用途包括:
- 将数据从一种格式转换为另一种格式
- 提供异常的通知
- 提供 Channel 变为活动的或者非活动的通知
- 提供当 Channel 注册到 EventLoop 或者从 EventLoop 注销时的通知
- 提供有关用户自定义事件的通知
你也可以根据需要通过添加或者移除ChannelHandler实例来修改ChannelPipeline。
二、内置的传输
Netty内置了一些可开箱即用的传输。因为并不是它们所有的传输都支持每一种协议,所以我们必须选择一个和应用程序所使用的协议相兼容的传输。
| 名称 | 包 | 描述 |
| NIO |
io.netty.channel.socket.nio |
使用java.nio.channels包作为基础——基于 选择器的方式 |
|
Epoll |
io.netty.channel.epoll |
由 JNI 驱动的 epoll()和非阻塞 IO。这个传输支持 只有在 Linux 上可用的多种特性,如 SO_REUSEPORT, 比 NIO 传输更快,而且是完全非阻塞的 |
| OIO |
io.netty.channel.socket.oio |
使用java.net包作为基础——使用阻塞流 |
| Local |
io.netty.channel.local |
可以在 VM 内部通过管道进行通信的本地传输 |
|
Embedded |
io.netty.channel.embedded |
Embedded 传输,允许使用 ChannelHandler 而又 不需要一个真正的基于网络的传输。这在测试你的 ChannelHandler 实现时非常有用 |
1、NIO
NIO提供了一个所有 I/O 操作的全异步的实现。它利用了自 NIO 子系统被引入 JDK 1.4 时便 可用的基于选择器的 API。选择器背后的基本概念是充当一个注册表,在那里你将可以请求在 Channel 的状态发生变化时得到通知。可能的状态变化有:
- 新的 Channel 已被接受并且就绪
- Channel 连接已经完成
- Channel 有已经就绪的可供读取的数据
- Channel 可用于写数据。
选择器运行在一个检查状态变化并对其做出相应响应的线程上,在应用程序对状态的改变做出响应之后,选择器将会被重置,并将重复这个过程。下表中的常量值代表了由 class java.nio.channels.SelectionKey 定义的位模式。这些位模式可以组合起来定义一组应用程序正在请求通知的状态变化集。
| 名称 | 描述 |
OP_ACCEPT |
请求在接受新连接并创建 Channel 时获得通知 |
OP_CONNECT |
请求在建立一个连接时获得通知 |
OP_READ |
请求当数据已经就绪,可以从 Channel 中读取时获得通知 |
OP_WRITE |
请求当可以向 Channel 中写更多的数据时获得通知。这处理了套接字缓冲区被完全填满时的情况,这种情况通常发生在数据的发送速度比远程节点可处理的速度更快的时候 |
对于所有 Netty 的传输实现都共有的用户级别 API 完全地隐藏了这些 NIO 的内部细节,下图展示了该处理流程:
2、Epoll——用于Linux的本地非阻塞传输
Netty 的 NIO 传输基于 Java 提供的异步/非阻塞网络编程的通用抽象。 虽然这保证了 Netty 的非阻塞 API 可以在任何平台上使用,但它也包含了相应的限制,因为 JDK 为了在所有系统上提供相同的功能,必须做出妥协。Linux作为高性能网络编程的平台,其重要性与日俱增,这催生了大量先进特性的开发,其 中包括epoll——一个高度可扩展的I/O事件通知特性。这个API自Linux内核版本 2.5.44(2002)被引入,提供了比旧的POSIX select和poll系统调用更好的性能,同时现在也是Linux上非阻塞网络编程的事实标准。Linux JDK NIO API使用了这些epoll调用。
Netty 的 OIO 传输实现代表了一种折中:它可以通过常规的传输 API 使用,但是由于它是建立在 java.net 包的阻塞实现之上的,所以它不是异步的。
Netty 提供了一个 Local 传输,用于在同一个 JVM 中运行的客户端和服务器程序之间的异步 通信。同样,这个传输也支持对于所有 Netty 传输实现都共同的 API。 在这个传输中和服务器 Channel 相关联的 SocketAddress 并没有绑定物理网络地址; 相反,只要服务器还在运行,它就会被存储在注册表里,并在 Channel 关闭时注销。因为这个传输并不接受真正的网络流量,所以它并不能够和其他传输实现进行互操作。因此,客户端希望连接到(在同一个 JVM 中)使用了这个传输的服务器端时也必须使用它。除了这个限制,它的使用方式和其他的传输一模一样。

5、Embeddad
Netty 提供了一种额外的传输,使得你可以将一组 ChannelHandler 作为帮助器类嵌入到 其他的 ChannelHandler 内部。通过这种方式,你将可以扩展一个 ChannelHandler 的功能, 而又不需要修改其内部代码。不足为奇的是,Embedded 传输的关键是一个被称为 EmbeddedChannel 的具体的 Channel 实现。在第 9 章中,我们将详细地讨论如何使用这个类来为 ChannelHandler 的实现创建单元 测试用例。

浙公网安备 33010602011771号