Netty 之 FileRegion 文件传输

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty 之 FileRegion 文件传输相关的知识,希望对你有一定的参考价值。

参考技术A Netty 传输文件的时候没有使用 ByteBuf 进行向 Channel 中写入数据,而使用的 FileRegion。下面通过示例了解下 FileRegion 的用法,然后深入源码分析 为什么不使用 ByteBuf 而使用 FileRegion。

从示例中可以看出 ChannelPipeline 中添加了自定义的 FileServerHandler()。
下面看下 FileServerHandler 的源码,其它几个 Handler 的都是 Netty 中自带的,以后会分析这些 Handler 的具体实现原理。

从 FileServerHandler 中可以看出,传输文件使用了 DefaultFileRegion 进行写入到 NiosocketChannel 里。
我们知道向 NioSocketChannel 里写数据,都是使用的 ByteBuf 进行写入。这里为啥使用 DefaultFileRegion 呢?

DefaultFileRegion 中有一个很重要的方法 transferTo() 方法

这里可以看出 文件 通过 FileChannel.transferTo 方法直接发送到 WritableByteChannel 中。
通过 Nio 的 FileChannel 可以使用 map 文件映射的方式,直接发送到 SocketChannel中,这样可以减少两次 IO 的复制。
第一次 IO:读取文件的时间从系统内存中拷贝到 jvm 内存中。
第二次 IO:从 jvm 内存中写入 Socket 时,再 Copy 到系统内存中。
这就是所谓的零拷贝技术。

从 ChannelOutboundBuffer 中获取 FileRegion 类型的节点。
然后调用 NioSocketChannel.doWriteFileRegion() 方法进行写入。

这里调用 FileRegion.transferTo() 方法,使用 基于文件内存映射技术进行文件发送。

Netty实战四之传输

流经网络的数据总是具有相同的类型:字节(网络传输——一个帮助我们抽象底层数据传输机制的概念)

Netty为它所有的传输实现提供了一个通用的API,即我们可以将时间花在其他更有成效的事情上。

我们将通过一个案例来对传输进行学习,应用程序只简单地接收连接,向客户端写 “Hi!” ,然后关闭连接。

1、不通过Netty使用OIO和NIO

先介绍JDK API的应用程序的阻塞(OIO)版本和异步(NIO)版本。

这段代码完全可以处理中等数量的并发客户端,但是随着应用程序变得流行起来,你会发现它并不能很好地伸缩到支撑成千上万的并发连入连接。你决定改用异步网络编程,但是很快就发现异步API是完全不同的,以至于现在你不得不重写你的应用程序。

Netty实战四之传输

Netty实战四之传输

Netty实战四之传输

虽然这段代码所做的事情与之前的版本完全相同,但是代码却截然不同,如果为了用于非阻塞I/O而重新实现这个简单的应用程序,都需要一次完全重写的话,那么不难想象,移植真正复杂的应用程序需要付出什么样的努力!

2、通过Netty使用OIO和NIO

Netty实战四之传输

Netty实战四之传输

3、非阻塞的Netty版本

Netty实战四之传输

因为Netty为每种传输的实现都暴露了相同的API,所以无论选用哪一种传输的实现,你的代码都仍然几乎不受影响,在所有的情况下,传输的实现都依赖于interface Channel、ChannelPipeline和ChannelHandler。

4、传输API

传输API的核心是interface Channel ,它被用于所有的I/O操作。Channel类的层次结构如图4-1(Channel接口的层次结构)所示。

Netty实战四之传输

如图所示,每个Channel都将会将分配一个ChannelPipeline和ChannelConfig。ChannelConfig包含了该Channel的所有配置设置,并且支持热更新。

由于特定的传输可能具有独特的设置,所以它可能会实现一个ChannelConfig的子类型。

由于Channel是独一无二的,所以为了保证顺序将Channel声明为java.lang.Comparable的子接口。因此,如果两个不同的Channel实例都返回相同的散列码,那么AbstractChannel中的compareTo()方法的实现将会抛出一个Error。

ChannelPiepeline持有所有将应用于入站和出站数据以及事件的ChannelHandler实例,这些ChannelHandler实现了应用程序用于处理状态变化以及数据处理的逻辑。

ChannelHandler的典型用途包括:

-将数据从一种格式转换为另一种格式

-提供异常的通知

-提供Channel变为活动的或者非活动的通知

-提供当Channel注册到EventLoop或者从EventLoop注销时的通知

-提供有关用户自定义事件的通知

拦截过滤器  ChannelPipeline实现了一种常见的设计模式——拦截过滤器(InterceptingFilter)。UNIX管道是另外一个熟悉的例子:多个命令被链接在一起,其中一个命令的输出端将连接到命令行中下一个命令的输入端。

你也可以根据需要通过添加或者移除ChannelHandler实例来修改ChannelPipeline。通过利用Netty的这项能力可以构建出高度灵活的应用程序。例如,每当STARTTLS协议被请求时,你可以简单地通过向ChannelPipeline添加一个适当的ChannelHandler(SslHandler)来按需地支持STARTTLS协议。

Netty实战四之传输

考虑一下写数据并将其冲刷到远程节点这样的常规任务,代码清单4-5演示了使用Channel.writeAndFlush()来实现这一目的。

Netty的Channel实现是线程安全的,因此你可以存储一个到Channel的引用,并且每当你需要向远程节点写数据时,都可以使用它,即使当时许多线程都在使用它。代码清单4-6展示了一个多线程写数据的简单例子,需要注意的是,消息将会被保证按顺序发送的。


以上是关于Netty 之 FileRegion 文件传输的主要内容,如果未能解决你的问题,请参考以下文章

Netty如何传输文件

Java之netty高性能

Netty网络编程实战3,使用Netty远程传输文件

Netty 4.0 SPDY 文件传输不工作

Netty实现类似Http File Server文件传输功能

Netty实战四之传输