(七)Netty与零拷贝
零拷贝基本介绍
- 零拷贝是网络编程的关键,很多性能优化都离不开
- 在Java程序中,常用的零拷贝有mmap(内存映射)和sendFile。那么,他们在OS里,到底是怎么样一个涉及?我们分析mmap和sendFile这两个两拷贝
- 另外我们看下NIO中如何使用零拷贝
传统IO数据读写
- Java传统IO和网络编程的一段代码
File file = new File("index.html");RandomAccessFile raf = new RandomAccessFile(file, "rw");byte[] arr = new byte[(int) file.length()];raf.read(arr);Socket socket = new ServerSocket(8080).accept();socket.getOutputStream().write(arr);
我们会调用 read 方法读取 index.html 的内容—— 变成字节数组,然后调用 write 方法,将 index.html 字节流写到 socket 中,那么,我们调用这两个方法,在 OS 底层发生了什么呢?我这里借鉴了一张其他文章的图片,尝试解释这个过程。
上图中,上半部分表示用户态和内核态的上下文切换。下半部分表示数据复制操作。下面说说他们的步骤:
read 调用导致用户态到内核态的一次变化,同时,第一次复制开始:DMA(Direct Memory Access,直接内存存取,即不使用 CPU 拷贝数据到内存,而是 DMA 引擎传输数据到内存,用于解放 CPU) 引擎从磁盘读取 index.html 文件,并将数据放入到内核缓冲区。
发生第二次数据拷贝,即:将内核缓冲区的数据拷贝到用户缓冲区,同时,发生了一次用内核态到用户态的上下文切换。
发生第三次数据拷贝,我们调用 write 方法,系统将用户缓冲区的数据拷贝到 Socket 缓冲区。此时,又发生了一次用户态到内核态的上下文切换。
第四次拷贝,数据异步的从 Socket 缓冲区,使用 DMA 引擎拷贝到网络协议引擎。这一段,不需要进行上下文切换。
write 方法返回,再次从内核态切换到用户态。
如你所见,复制拷贝操作太多了。如何优化这些流程?
mmap优化
mmap 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户空间的拷贝次数。如下图:
如上图,user buffer 和 kernel buffer 共享 index.html。如果你想把硬盘的 index.html 传输到网络中,再也不用拷贝到用户空间,再从用户空间拷贝到 Socket 缓冲区。
现在,你只需要从内核缓冲区拷贝到 Socket 缓冲区即可,这将减少一次内存拷贝(从 4 次变成了 3 次),但不减少上下文切换次数。
sendFile
那么,我们还能继续优化吗? Linux 2.1 版本 提供了 sendFile 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 Socket Buffer,同时,由于和用户态完全无关,就减少了一次上下文切换。
如上图,我们进行 sendFile 系统调用时,数据被 DMA 引擎从文件复制到内核缓冲区,然后调用 write 方法时,从内核缓冲区进入到 Socket,这时,是没有上下文切换的,因为都在内核空间。
最后,数据从 Socket 缓冲区进入到协议栈。此时,数据经过了 3 次拷贝,3 次上下文切换。那么,还能不能再继续优化呢? 例如直接从内核缓冲区拷贝到网络协议栈?
实际上,Linux 在 2.4 版本中,做了一些修改,避免了从内核缓冲区拷贝到 Socket buffer 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。具体如下图:
现在,index.html 要从文件进入到网络协议栈,只需 2 次拷贝:第一次使用 DMA 引擎从文件拷贝到内核缓冲区,第二次从内核缓冲区将数据拷贝到网络协议栈;内核缓存区只会拷贝一些 offset 和 length 信息到 SocketBuffer,基本无消耗。
零拷贝的再次理解
- 我们说零拷贝,是从操作系统的角度来说的。因为内核缓冲区之间,没有数据是重复的(只有 kernel buffer 有一份数据,sendFile 2.1 版本实际上有 2 份数据,算不上零拷贝)。例如我们刚开始的例子,内核缓存区和 Socket 缓冲区的数据就是重复的。
- 零拷贝不仅仅带来更少的数据复制,还能带来其他的性能优势,例如更少的上下文切换,更少的 CPU 缓存伪共享以及无 CPU 校验和计算。
mmap和sendFile的区别
- mmap适合小数据量读写,sendFile适合大文件传输
- mmap需要4次上下文切换,3次数据拷贝;sendFile需要3次上下文切换,最少2次数据拷贝
- sendFile可以利用DMA方式,减少CPU拷贝,mmap则不能(必须从内核拷贝到Socket缓冲区)
在选择上:rocketMQ 在消费消息时,使用了 mmap。kafka 使用了 sendFile。
NIO零拷贝案例
案例要求:
- 使用传统的IO方法传递一个大文件
- 使用NIO零拷贝方式传递一个大文件(transferTo)
- 看看两种传递方式耗时时间分别是多少
传统方式
package com.example.demo.netty.zerocopy;import java.io.DataInputStream;
import java.net.ServerSocket;
import java.net.Socket;public class OldIOServer {public static void main(String[] args) throws Exception {ServerSocket serverSocket = new ServerSocket(7001);while (true) {Socket socket = serverSocket.accept();DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());try {byte[] byteArray = new byte[4096];while (true) {int readCount = dataInputStream.read(byteArray, 0, byteArray.length);if (readCount == -1) {break;}}} catch (Exception e) {e.printStackTrace();}}}
}
package com.example.demo.netty.zerocopy;import java.io.*;
import java.net.Socket;public class OldIOClient {public static void main(String[] args) throws Exception {Socket socket = new Socket("localhost", 7001);File file = new File("bigFile.war");InputStream inputStream = new FileInputStream(file);DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());byte[] byteArray = new byte[4096];long readCount = 0;long total = 0;long startTime = System.currentTimeMillis();while ((readCount = inputStream.read(byteArray)) >= 0) {total += readCount;dataOutputStream.write(byteArray);}System.out.println("发送总字节数: " + total + ",耗时:" + (System.currentTimeMillis() - startTime));dataOutputStream.close();socket.close();inputStream.close();}
}
NIO零拷贝
package com.example.demo.netty.zerocopy;import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;public class NewIOServer {public static void main(String[] args) throws Exception {InetSocketAddress address = new InetSocketAddress(7001);ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();ServerSocket serverSocket = serverSocketChannel.socket();serverSocket.bind(address);ByteBuffer byteBuffer = ByteBuffer.allocate(4096);while (true) {SocketChannel socketChannel = serverSocketChannel.accept();int readCount = 0;while (-1 != readCount) {try {readCount = socketChannel.read(byteBuffer);} catch (Exception e) {e.printStackTrace();}byteBuffer.rewind();//倒带position=0 mark作废}}}
}
package com.example.demo.netty.zerocopy;import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;public class NewIOClient {public static void main(String[] args) throws Exception {SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost", 7001));String filename = "bigFile.war";//得到一个文件channelFileChannel fileChannel = new FileInputStream(filename).getChannel();//准备发送long startTime = System.currentTimeMillis();//在linux下 一个transferTo 方法就可以完成传输//在windows下 一个transferTo只能发送8M,就需要分段传输文件,而且要主要//传输时的位置//transferTo 底层使用到零拷贝long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);System.out.println("发送总字节数: " + transferCount + ",耗时:" + (System.currentTimeMillis() - startTime));//关闭fileChannel.close();}
}
AIO基本介绍
- JDK7 引入了,Asynchronous I/O,即AIO ,在进行IO编程中,常用到两种模式:Reactor 和 Proactor, Java 的NIO就是Reactor,当有事件触发时,服务器端得到通知进行相应的处理
- AIO 即NIO2.0, 叫异步非阻塞IO。AIO引入异步通道的概念,采用了Proactor模式,简化了程序编写,有效的请求才启动线程,他的特点是,先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用
- 目前AIO还没有广泛应用,Netty也是基于NIO,而不是AIO,因此就不在这里讲AIO了,有兴趣的可以链接一下http://www.52im.net/thread-306-1-1.html
BIO、NIO、AIO对比
(七)Netty与零拷贝相关推荐
- netty的零拷贝、架构设计、ByteBuf扩容机制详解
文章目录 1. netty高并发架构设计精髓 ①:主从.Reactor线程模型 ②:NIO多路复用非阻塞 ③:无锁串行化设计思想 ④:高可用.可扩展架构 ⑤:直接内存和零拷贝 ⑥:ByteBuf内存池 ...
- 【Netty】零拷贝案例 ( transferTo | transferFrom )
文章目录 一. 案例需求 二. 传统 BIO 拷贝案例 三. 零拷贝案例 服务器端 四. 零拷贝案例 客户端 五. 零拷贝案例 运行与分析 一. 案例需求 给出两个案例 , 一个是 使用普通的 BIO ...
- 【Netty】零拷贝(zero-copy)
目录 1. 零拷贝技术实现 2. 传统读取IO流的操作 2.1 读操作 2.2 写操作 2.3 MMAP+write 2.4 Sendfile 3. 零拷贝应用场景 很多更新的技术在宣传的时候,都会提 ...
- 【48期】盘点Netty面试常问考点:什么是 Netty 的零拷贝?
点击上方蓝色"java大数据修炼之道", 选择"设为星标" 每晚九点: 技术干货
- 80-15-020-原理-零拷贝-Netty零拷贝的原理
1.概述 Netty 的零拷贝 1. 传统意义的拷贝 是在发送数据的时候,传统的实现方式是: File.read(bytes) Socket.send(bytes) 这种方式需要四次数据拷贝和四次上下 ...
- 彻底搞懂Netty高性能之零拷贝
作为Java网络编程学习者,不仅要知道NIO,还一定要学习Mina和Netty这两个优秀的网络框架.作为上一篇NIO效率高的原理之零拷贝与直接内存映射的补充,本文将针对Netty的零拷贝特性进行详细分 ...
- netty零拷贝之CompositeByteBuf
目录 一. 背景简介 二. netty零拷贝 一.背景简介 在TCP网络数据传输过程中,数据包有可能被分割为独立的几个数据包进行发送,对于服务器接收端来说,单个的数据包是没有任何意义的,只有将这些数据 ...
- 四十七、Netty零拷贝
零拷贝的定义 Zero-copy, 就是在操作数据时, 不需要将数据 buffer 从一个内存区域拷贝到另一个内存区域. 因为少了一次内存的拷贝, 因此 CPU 的效率就得到的提升. 在 OS 层面上 ...
- 零拷贝技术在 Java 中为何这么牛?
[CSDN 编者按]大家在学计算机的过程中是否会遇到"零拷贝"这样的字眼,本文针对这一技术为大家提供详细的概念解释,并对其产出的意义及其在Java中的应用进行说明. 责编 | 欧阳 ...
最新文章
- java 文件拷贝文件怎么打开_java如何拷贝文件
- 通过卫星图像预测区域内降雨范围和降雨量
- xxxx无法转换为java.lang.Class<? extends javax.validation.Payload>
- python制作文本编辑器_Python小实战:制作文本编辑器
- 飞鸽传书内部排序算法的性能比较
- 零日攻击的原理与防范方法
- (转)详解Windows Hash
- 删除单链表中指针q指向的结点
- 查看dll库导出库的函数接口
- 【收益管理】单资源容量控制(2)先从报童模型谈起!
- 三维点云课程(一)——点云基础介绍
- MATLAB实现imrotate函数
- 从零开始学习SpringCloud
- 如何给计算机d盘加密码,怎样给电脑文件夹加密
- wps2022无法加载此加载项程序mathpage.wll
- Python攻城师的成长——ORM(choices字段)、AJAX
- 程序员的可迁移技能和经验
- 新能源汽车比亚迪唐220V放电系统粗解,以及为什么需要支持V2G?
- cisco3560及二层交换机配置vlan及常用命令
- 我也和 chatGPT 聊了聊
热门文章
- Intel Core Enhanced Core架构/微架构/流水线 (9) - 执行单元发射口旁路时延
- Intel Core Enhanced Core架构/微架构/流水线 (2) - 代表处理器
- 自定义指令监听多个div_Vue 3 | 自定义指令的新玩法
- CentOS7环境下搭建Kafka
- Coding the Matrix Week 3 The Matrix 矩阵
- 移动终端CPU、GPU浅析
- [UOJ50]链式反应
- 【软件工程实践 · 团队项目】 第二次作业
- 淘宝内核月报 2017
- 无法将 lambda 表达式 转换为类型“System.Delegate”,因为它不是委托类型