Netty如何传输文件

Netty如何传输文件,第1张

首先发送端将file包装成filereigon传输,其内部会循环的将文件发送到接收端

而接收端接收到的都是bytebuf,然后我们接收端可以将其写到filechannel中。这样在接收端就可以写成文件了,从这样看我们的内存也不会因为传输的文件大而爆掉。因为我们底层是依靠transferTo的transferTo去循环发送文件数据

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的零拷贝技术主要基于以下几点:

1. 堆外内存,也叫直接内存

2. Composite Buffers

3. 文件传输基于linux的sendfile机制

Linux的设计的初衷:给不同的 *** 作给与不同的“权限”。Linux *** 作系统就将权限等级分为了2个等级,分别就是 内核态和用户态。

内核态是属于cpu的特权工作模式,可以 *** 作计算机设备中的任何元件,包括网卡、硬盘、内存等等。

用户态是应用程序的工作模式,只能 *** 作已申请的内存空间,无法 *** 作外围设备。当应用程序需要与网卡、硬盘等外围设备进行交互时,需要通过系统提供的接口,来调用外围设备。

堆内存中的数据如果需要发送到外围设备,需要调用系统的接口,将数据拷贝到堆外内存中,发送到外围设备中。

而Netty的ByteBuffer不经过堆内存,直接在堆外内存中进行读写,省去一步拷贝 *** 作。

需要注意的是,堆外内存只能通过主动调用回收或者Full GC回收,如果使用不当,容易造成内存溢出。

Composite Buffers

Netty提供了Composite Buffers来组合多个buffer。传统的buffer如果要合并的话,需要新建一个buffer,将原来的buffer拷贝到新的buffer中进行合并。而Composite Buffers相当于buffer的集合,保存了每个buffer对象,使物理的buffer合并变为逻辑上的buffer的合并。

文件传输

Netty的文件传输是依赖于 *** 作系统的零拷贝技术。

一般我们读取文件都是调用 *** 作系统接口, *** 作系统在应用程序读取文件时,会首先判断文件是否在内核缓冲区中,如果不在,则需要将文件从磁盘或socket读取到内核缓冲区中。

在写入文件时, *** 作系统会将文件先写入内核缓冲区,再写入到socket中。

我们传统做文件拷贝或传输时,会先在应用程序内存中构建一个缓冲区,通过这个缓冲区与 *** 作系统做数据交换。这样无疑会增加了文件的多次拷贝。

传统的文件传输过程如下:

1. 构建byte[]数组来缓冲文件

2. 切换到内核态,将文件先在内核缓冲区中缓存

3. 将内核缓冲区的数据拷贝到应用程序缓冲区的byte[]数组中

4. 切换回用户态

5. 执行写入 *** 作,切换回内核态

6. 将数据再拷贝一份到内核中的socket缓冲区

7. 切换回用户态

8. *** 作系统将数据异步刷新到网卡

传统的文件传输过程,会造成 *** 作系统在用户态和内核态多次切换,非常影响性能。

而linux在内核2.1中引入了sendfile *** 作,过程如下:

1. 读取数据时,sendfile系统调用导致文件内容通过DMA模块被复制到内核缓冲区中

2. 写入数据时,数据直接复制到socket关联的缓冲区(linux内核2.4已删除这一步,取而代之的是,只有记录数据位置和长度的描述符被加入到socket缓冲区中。DMA模块将数据直接从内核缓冲区传递给协议引擎)

3. 最后将socket buffer中的数据copy到网卡设备中(protocol buffer)发送

netty的FileRegion 包下的FileChannel.tranferTo即是基于sendfile机制来实现文件传输的


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/9631440.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-30
下一篇 2023-04-30

发表评论

登录后才能评论

评论列表(0条)

保存