Netty-NIO与零拷贝
零拷贝基本介绍
- 零拷贝是网络编程的关键,很多性能优化都离不开
- 在Java程序中,常用的零拷贝有mmap(内存映射)和sendFile
- 零拷贝并非是不拷贝,而是没有CPU拷贝
- 所说的零拷贝,是从操作系统的角度来说的。因为内核缓冲区之间,没有数据是重复的(只有kernel buffer有一份数据)
- 零拷贝不仅带来更少的数据复制,还能带来其它的性能优势,例如更少的上下文切换,更少的CPU缓存伪共享以及无CPU校验和计算
传统IO数据读写
Java传统IO和网络编程的一段代码
1
2
3
4
5
6File file = new File("test.txt");
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);
mmap优化(三次拷贝,三次交换)
- mmap通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户控件的拷贝次数
sendFile优化(三次拷贝,两次交换)
- Linux2.1版本提供了sendFile函数,其基本原理:数据根本不经过用户态,直接从内核缓冲区进入到Socket Buffer,同时,由于和用户态完全无关,就减少了一次上下文切换
- Linux2.4版本中,做了一些修改,避免了从内核缓冲区拷贝到Socket buffer的操作,从而再一次减少了数据拷贝(两次拷贝,两次交换)(实际上有一次CPU拷贝,从kernel buffer到socket buffer,但拷贝的信息很少,如length、offset,消耗低,可以忽略)
mmap和sendFile的区别
- mmap适合小数据量读写,sendFile适合大文件传输
- mmap需要4次上下文切换,3次数据拷贝,sendFile需要3次上下文切换,最少2次数据拷贝
- sendFile可以利用DMA方式,减少CPU拷贝,mmap则不能(必须从内核拷贝到Socket缓冲区)
NIO零拷贝案例
1 | public class Server { |
1 | public class Client { |
AIO基本介绍
- JDK 7引入了Asynchronous I/O,即AIO。在进行I/O编程中,常用到两种模式Reactor和Proactor,Java的NIO就是Reactor,当有事件触发时,服务器得到通知,进行相应的处理
- AIO即NIO2.0,叫做异步不阻塞的IO,AIO引入异步通道的概念,采用了Proactor模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用
- 目前AIO还没有广泛应用,Netty也是基于NIO,而不是AIO
BIO、NIO、AIO对比表
BIO | NIO | AIO | |
---|---|---|---|
IO模型 | 同步阻塞 | 同步非阻塞(多路复用) | 异步非阻塞 |
编程难度 | 简单 | 复杂 | 复杂 |
可靠性 | 差 | 好 | 好 |
吞吐量 | 低 | 高 | 高 |
举例说明:
- 同步阻塞:到理发店理发,就一直等理发师,直到轮到自己理发
- 同步非阻塞:到理发店理发,发现前面有其它人理发,给理发师说下,先做其它事情,一会过来看是否轮到自己
- 异步非阻塞:给理发师打电话,让理发师上门服务,自己做其它事件,理发师自己来家给你理发(或到理发店理发,发现前面有其它人理发,告诉理发师,先做其它事,轮到自己时,理发师来通知)