NIO--零拷贝、AIO

零拷贝

  • 零拷贝是网络编程的关键,很多性能优化都离不开。
  • 在 Java 程序中,常用的零拷贝有 mmap(内存映射) 和 sendFile。

传统IO数据读写:
Java 传统 IO 和 网络编程的一段代码
在这里插入图片描述
传统IO模型:
在这里插入图片描述

mmap优化:

  • mmap 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网 络传输时,就可以减少内核空间到用户空间的拷贝次数。
  • mmap 示意图

在这里插入图片描述

sendFile优化:
Linux 2.1 版本 提供了 sendFile 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到
Socket Buffer,同时,由于和用户态完全无关,就减少了一次上下文切换

在这里插入图片描述

  • 零拷贝从操作系统角度,是没有cpu拷贝
  • Linux 在 2.4 版本中,做了一些修改,避免了从内核缓冲区拷贝到 Socket buffer 的操作,直接拷贝到协议栈,
    从而再一次减少了数据拷贝。具体如下图

在这里插入图片描述
这里其实有一次cpu拷贝kernel buffer -> socket buffer但是,拷贝的信息很少,比如 lenght , offset , 消耗低,可以忽略

mmap 和 sendFile 的区别:

  1. mmap 适合小数据量读写,sendFile 适合大文件传输。
  2. mmap 需要 4 次上下文切换,3 次数据拷贝;sendFile 需要 3 次上下文切换,最少 2 次数据拷贝。
  3. sendFile 可以利用 DMA 方式,减少 CPU 拷贝,mmap 则不能(必须从内核拷贝到 Socket 缓冲区)。

NIO零拷贝案例

//服务器
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);

        //创建buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(4096);

        while (true) {
            SocketChannel socketChannel = serverSocketChannel.accept();

            int readcount = 0;
            while (-1 != readcount) {
                try {

                    readcount = socketChannel.read(byteBuffer);

                }catch (Exception ex) {
                   // ex.printStackTrace();
                    break;
                }
                //
                byteBuffer.rewind(); //倒带 position = 0 mark 作废
            }
        }
    }
}
public class NewIOClient {
    public static void main(String[] args) throws Exception {

        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost", 7001));
        String filename = "protoc-3.6.1-win32.zip";

        //得到一个文件channel
        FileChannel 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

  1. JDK 7 引入了 Asynchronous I/O,即 AIO。在进行 I/O 编程中,常用到两种模式:Reactor 和 Proactor。Java 的 NIO 就是 Reactor,当有事件触发时,服务器端得到通知,进行相应的处理
  2. AIO 即 NIO2.0,叫做异步不阻塞的 IO。AIO 引入异步通道的概念,采用了 Proactor 模式,简化了程序编写, 有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接 数较多且连接时间较长的应用
  3. 目前AIO还没有广泛应用,Netty也是基于NIO,而不是AIO,因此我们就不详解AIO了,有兴趣的同学可 以参考 <<Java 新一代网络编程模型 AIO 原理及 Linux 系统 AIO 介绍 >>
    http://www.52im.net/thread-306-1-1.html

BIO、NIO、AIO:
在这里插入图片描述

发布了766 篇原创文章 · 获赞 2129 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/cold___play/article/details/104335247