04 传输

点击查看 《Netty in Action》笔记目录

本文是对《Netty in Action》第4章内容的笔记和翻译,主要内容包括:

  • OIO:阻塞传输
  • NIO:异步传输
  • 本地传输:和 JVM 异步交互
  • 测试你的 ChannelHandler

案例学习:传输迁移

不通过 Netty 使用 OIO 和 NIO

我们将要展示基于 JDK API 的阻塞(OIO)和异步(NIO)应用版本。下面展示了阻塞实现版本。


下面展示了非阻塞版本。


通过 Netty 使用 OIO 和 NIO

非阻塞 Netty 版本

因为 Netty 对每一种传输实现都暴露了相同的 API,所以无论你采用哪一种传输,你的代码几乎不用修改。

传输 API

传输 API 的核心是 Channel 接口,所有的 I/O 操作都会使用这个接口。Channel 类的继承关系如图4.1所示。

Channel 中组合了 ChannelPipelineChannelConfigChannelConfig 保存了 Channel 所有的配置,并且支持热更新。

由于 Channel 是独一无二的,所以 Channel 继承了 java.lang.Comparable 接口,从而保证对象的有序。因此,如果两个 Channel 实例返回相同的 hash 值,那 AbstractChannelcompareTo() 的实现将会抛出 Error

图4.1 Channel接口继承关系

ChannelHandler 常用于:

  • 转换数据格式。
  • 提供异常通知。
  • 提供 Channel 被激活或者关闭的通知。
  • 提供 ChannelEventLoop 中注册和取消注册的通知。
  • 提供用户定义事件的通知。

拦截过滤器
ChannelPipeline 实现了一个常用设计模式:拦截过滤器。UNIX 管道是这个模式的另一个典型例子:命令被链式编排,每个命令的输出会作为接下来一个命令的输入。

你同样可以在运行时按照你的需求为 ChannelPipeline 添加或删除 ChannelHandler

除了可以使用 ChannelPipelineChannelConfig 之外,你还可以使用 Channel 的方法,下表展示了一些常用的方法。

方法名称 描述
eventLoop 返回与 Channel 绑定的 EventLoop
pipeline 返回与 Channel 绑定的 ChannelPipeline
isActive 如果 Channel 是激活(active)的,返回 true。 Active 的含义取决于下层的传输协议。例如, Socket 中的 active 是指与远端建立连接;Datagram 中的 active 是指 Datagram 打开。
localAddress 返回本地的 SocketAddress.
remoteAddress 返回远端的 SocketAddress.
write 将数据写到远端。数据会经过 ChannelPipeline,并将会进行排队,直到刷新时才会发送。
flush 将之前写的数据刷新到底层的传输协议中,例如:Socket
writeAndFlush 先调用 write() ,然后调用 flush()

下图展示了如何利用 Channel.writeAndFlush() 实现最常见的发送数据到远端的任务。

Netty 的 Channel 实现是线程安全的,所以你可以存储 Channel 的一个引用,并且在你需要发送数据的时候使用它,即使在多线程环境中也没有关系。

包含的传输

下表展示了 Netty 中提供的传输。

名称 所在的包 描述
NIO io.netty.channel.socket.nio java.nio.channels 包为基础,是一个基于 selector 的实现方案。
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 一个嵌入式传输协议。通过它,你可以在虚拟的传输协议上使用 ChannelHandler。这在测试 ChannelHandler 实现时很有用。

NIO — 非阻塞 I/O

Selector 背后基本的原理是:提供一个注册器,通过注册感兴趣的事件,当 Channel 状态改变的时候你可以得到通知。可能的状态改变包括:

  • 新的 Channel 被接收到并且已经准备好。
  • Channel 连接已经完成。
  • Channel 已经准备好为读操作提供数据。
  • Channel 已经准备好,可以写入数据了。

下表展示了 java.nio.channels.SelectionKey 类定义的一些模式。

方法名称 描述
OP_ACCEPT 当一个新的请求被接受并且 Channel 被创建的时候,会进行通知。
OP_CONNECT 当连接建立的时候,会进行通知。
OP_READ 当可以从 Channel 中读取数据的时候,会进行通知。
OP_WRITE 当可以往 Channel 中写入更多数据的时候,会进行通知。这个事件在 socket 缓存被填满的时候发生,通常意味着数据发送的速率操过了远端的处理能力。

NIO 内部实现的细节,在所有的 Netty 用户级别的 API 中都被隐藏了。图4.2 展示了处理的数据流。

图4.2 选择和处理状态改变

零拷贝
零拷贝这个特性目前只在 NIO 和 Epoll 传输中被支持。通过它可以使你快速并高效地从一个文件系统转移数据到网络中,不需要从内核空间拷贝到用户空间,这大大提高了 FTP、HTTP 等协议的传输效率。这个特性并不是被所有的操作系统支持。确切地说,它并不适用于采用加密或压缩的文件系统,只有文件的原始数据可以被转移。但对于已经加密好的文件是可以被传输的。

Epoll — 针对 Linux 的原生非阻塞传输

Netty 为 Linux 系统提供了一个使用 epoll 的 NIO API。在某种程度上,这与 Netty 本身的设计更加一致,并且比中断的实现方式开销更少。如果你的应用准备在 Linux 上使用,那么可以考虑这个版本,你会发现在高负载的情况下,它的性能表现会好于 JDK 本生的 NIO 实现。

这个传输和图4.2中展示的传输语义相同,并且它的使用是很简单的,可以参考 list 4.4。为了使用 epoll 版的 NIO,可以将 NioEventLoopGroup 替换为 EpollEventLoopGroup,将 NioServerSocketChannel.class 替换为 EpollServerSocketChannel.class

OIO — 阻塞型 I/O

图4.3 OIO 处理逻辑

你可能会对此感到好奇:Netty 是如何通过相同的 API 来支持异步传输的 NIO?答案是 Netty 使用了 SO_TIMEOUT Socket 标志。 这个标志表明等待 I/O 操作完成的最大毫秒数。如果操作在指定的时间间隔内没有完成,那么将会抛出 SocketTimeoutException。Netty 会捕捉这个异常,并继续循环处理。

Local — 通过本地传输与 JVM 交互

Netty 为同一个 JVM 上的客户端和服务端的异步交互提供了一个本地传输。

因为本地传输并没有真正的网络传输,所以它不能和其它传输协议一起协作。因此,一个客户端如果想通过这个传输来连接服务端的话(在同一个 JVM 上),服务端也必须使用这个传输。除了这个限制,它的使用和其它传输协议是一样的。

嵌入传输

Netty 还提供了一个传输,可以允许你嵌入在 ChannelHandler 中嵌入一个帮助类 ChannelHandler。在这种模式下,你可以扩展 ChannelHandler 的功能,而不用内部的代码。

传输使用例子

下表展示了当前版本中传输支持的协议。

Transport TCP UDP SCTP UDT
NIO 支持 支持 支持 支持
Linux 上的 Epoll 支持 支持 - -
OIO 支持 支持 支持

在 Linux 上开启 SCTP
SCTP 需要内核支持并且需要安装一些用户库。例如,在 Ubuntu 上可以使用下面的命令:

# sudo apt-get install libsctp1

在 Fedora 上,你需要使用 yum:

# sudo yum install kernel-modules-extra.x86_64 lksctp-tools.x86_64

请阅读你的 Linux 发布版本的手册,了解如何开启 SCTP。

下面是你可能会遇到的一些用例。

  • 基于非阻塞的代码:如果你在你的代码中不阻塞调用,或者你要限制阻塞的调用,推荐使用 NIO(在 Linux 上使用 epoll)。NIO/epoll 主要是用于处理很多并发的连接,在数量较少的时候变现的也不错,尤其是在多个连接共享线程的情况下。
  • 基于阻塞的代码:正如我们之前提过,如果你的代码很依赖阻塞 I/O,并且你应用具有相应的设计,那么你直接从阻塞 I/O 切换到 Netty 的 NIO 传输,可能会遇到麻烦。最好不要直接重写代码来适应新的 I/O, 可以考虑一个阶段性的迁移:从 OIO 开始,迁移到 NIO(或者你使用 Linux 的话就是 epoll)。
  • 在同一个 JVM 上进行交互:如果要在同一个 JVM 上进行交互不需要使用网络,使用本地传输再恰当不过了。这可以在使用 Netty 代码的同时消除真实网络的开销。如果你需要在真实网络上使用该服务,你只需将传输替换为 NIO 或者 OIO。
  • 测试你的 ChannelHandler 实现:如果你想为你的 ChannelHandler 实现写单元测试,可以考虑使用嵌入传输。这可以使得你在测试实现的时候不用创建很多的模拟对象。你写的类还是会和通用的 API 事件流保持一致,保证 ChannelHandler 在真实传输上表现正确。

下表总结了我们提到过的用例。

应用需求 推荐使用的协议
非阻塞或者普通的尝试 NIO(或者在 Linux 上使用 epoll)
阻塞 OIO
在同一个 JVM 上交互 Local
测试你的 ChannelHandler 实现 Embedded
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容