1.本文总结自B站《netty-尚硅谷》,很不错;
2.本文部分内容参考自 NIO效率高的原理之零拷贝与直接内存映射 - 腾讯云开发者社区-腾讯云
【图解】
小结: 上述过程中,操作系统底层 有4次用户态与内核态的切换,2次cpu拷贝(DMA拷贝不占CPU,不计入);文件读写(拷贝)性能较低;
补充:
1)零拷贝 :
2)零拷贝流程图 :
【图解】
【小结】
NIO零拷贝适用于以下场景:
注意:字节缓冲 4M (字节缓冲大小会影响传输性能,当然了,一定条件下,缓冲越大越好);
1)服务器:
/**
* @Description 传统IO服务器
* @author xiao tang
* @version 1.0.0
* @createTime 2022年08月20日
*/
public class OldIOServer {
public static void main(String[] args) throws IOException {
// 服务器监听端口 7001
ServerSocket serverSocket = new ServerSocket(7001);
while (true) {
// 阻塞式等待客户端请求链接
Socket socket = serverSocket.accept();
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
try {
byte[] byteArr = new byte[4096];
// 读取客户端的数据到字节数组
while (dataInputStream.read(byteArr, 0, byteArr.length) != -1) ;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
2)客户端:
/**
* @Description 传统IO客户端
* @author xiao tang
* @version 1.0.0
* @createTime 2022年08月20日
*/
public class OldIOClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 7001);
// 传输 一个 zip文件
InputStream inputStream = new FileInputStream("D:\\cmb\\studynote\\netty\\temp\\springboot.zip");
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
byte[] buffer = new byte[4096];
long readCount;
long total = 0;
long startTime = System.currentTimeMillis();
while ((readCount = inputStream.read(buffer)) >= 0) {
total += readCount;
dataOutputStream.write(buffer);
}
long cost = System.currentTimeMillis() - startTime;
System.out.println("发送总字节数 " + total + ", 耗时 = " + cost);
// 关闭资源
dataOutputStream.close();
socket.close();
inputStream.close();
}
}
3)效果:
发送总字节数 4491230, 耗时 = 50
注意:字节缓冲 4M (字节缓冲大小会影响传输性能,当然了,一定条件下,缓冲越大越好);
1)服务器:
/**
* @Description nio实现零拷贝服务器
* @author xiao tang
* @version 1.0.0
* @createTime 2022年08月20日
*/
public class ZeroCopyNIOServer {
public static void main(String[] args) throws IOException {
// 服务器套接字通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 绑定端口
serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", 7001));
ByteBuffer buffer = ByteBuffer.allocate(4096);
while (true) {
// 等待客户端连接
SocketChannel socketChannel = serverSocketChannel.accept();
// 读取数据
while (socketChannel.read(buffer) != -1) {
// 缓冲倒带, 设置 position=0, mark作废
buffer.rewind();
}
}
}
}
2)客户端:
/**
* @Description nio实现零拷贝服务器
* @author xiao tang
* @version 1.0.0
* @createTime 2022年08月20日
*/
public class ZeroCopyNIOClient {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 7001));
FileChannel fileChannel = new FileInputStream("D:\\cmb\\studynote\\netty\\temp\\springboot.zip").getChannel();
long startTime = System.currentTimeMillis();
// 在 linux 下,一次调用 transferTo 方法就可以完成传输
// 在 window下,一次调用 transferTo 只能发送 8M,如果大于8M,则需要分段传输文件
// transferTo 底层就用到了 零拷贝
long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
long cost = System.currentTimeMillis() - startTime;
System.out.println("客户端发送数据成功,耗时= " + cost + ", 传输的字节总数 = " + transferCount);
}
}
3)效果:
客户端发送数据成功,耗时= 10, 传输的字节总数 = 4491230
4)补充: 关于 FileChannel.transferTo 方法
5)transferTo方法底层使用的是 sendfile 系统调用(零拷贝)
【表】传统IO与NIO零拷贝传输性能对比 (文件大小 4M)
Java IO类型 |
传输耗时 |
备注 |
传统IO |
50ms |
|
NIO零拷贝 |
10ms |
性能更优 |