Netty3-NIO与零拷贝

1.传统IO数据读写

  1. Java 传统 IO 和 网络编程的一段代码
File 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);

传统IO 进行四次拷贝,三次切换(仅仅是读写)

mmap 优化

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

sendFile 优化1

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

所谓零拷贝是操作系统角度看,是没有CPU拷贝

sendFile 优化

  1. Linux 在 2.4 版本中,做了
    一些修改,避免了从内核
    缓冲区拷贝到 Socket
    buffer 的操作,直接拷贝到
    协议栈,从而再一次减少
    了数据拷贝。

小结:
这里其实有 一次cpu 拷贝
kernel buffer -> socket buffer
但是,拷贝的信息很少,比如
lenght , offset , 消耗低,可以
忽略

零拷贝的再次理解

  1. 我们说零拷贝,是从操作系统的角度来说的。因为内核缓冲区之间,没有数据是
    重复的(只有 kernel buffer 有一份数据)。
  2. 零拷贝不仅仅带来更少的数据复制,还能带来其他的性能优势,例如更少的上下
    文切换,更少的 CPU 缓存伪共享以及无 CPU 校验和计算。

零拷贝案例-Server

package com.atguigu.nio.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);

        //创建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 作废
            }
        }
    }
}

零拷贝案例-CLient

package com.atguigu.nio.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 = "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();

    }
}

BIO、NIO、AIO对比表

在这里插入图片描述

  1. 同步阻塞:到理发店理发,就一直等理发师,直到轮到自己理发。
  2. 同步非阻塞:到理发店理发,发现前面有其它人理发,给理发师说下,先干其他事情,一会过来看是否轮到自己.
  3. 异步非阻塞:给理发师打电话,让理发师上门服务,自己干其它事情,理发师自己来家给你理发

NIO零拷贝关键方法

将此通道的文件中的字节传输到给定的可写字节通道。
尝试读取从该通道文件中给定位置开始的count个字节,并将它们写入目标通道。此方法的调用可能会也可能不会传输所有请求的字节;是否这样做取决于通道的性质和状态。如果此通道的文件从给定位置开始包含少于count个字节,或者如果目标通道是非阻塞的并且其输出缓冲区中的空闲字节少于count个,则传输的字节数少于请求的字节数。
此方法不会修改此通道的位置。如果给定位置大于文件的当前大小,则不传输任何字节。如果目标通道有一个位置,则从该位置开始写入字节,然后该位置增加写入的字节数。
这种方法可能比从该通道读取并写入目标通道的简单循环更有效。许多操作系统可以直接将字节从文件系统缓存传输到目标通道,而无需实际复制它们。
参数:
position – 文件中开始传输的位置;必须是非负数
count – 要传输的最大字节数;必须是非负数
target – 目标通道
返回值:
实际传输的字节数,可能为零
抛出:
IllegalArgumentException – 如果参数的先决条件不成立
NonReadableChannelException – 如果未打开此通道进行读取
NonWritableChannelException – 如果没有打开目标通道进行写入
ClosedChannelException – 如果此通道或目标通道已关闭
AsynchronousCloseException – 如果另一个线程在传输过程中关闭任一通道
ClosedByInterruptException – 如果另一个线程在传输过程中中断当前线程,从而关闭两个通道并设置当前线程的中断状态
IOException – 如果发生其他一些 I/O 错误
 public abstract long transferTo(long position, long count,
                                    WritableByteChannel target)
        throws IOException;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值