聊聊 传统IO和网络IO

 

IO 模型

传统 IO读写

       磁盘IO主要的延时是由(以15000rpm硬盘为例): 机械转动延时(机械磁盘的主要性能瓶颈,平均为2ms) + 寻址延时(2~3ms) + 块传输延时(一般4k每块,40m/s的传输速度,延时一般为0.1ms) 决定。(平均为5ms)

 /**
     * 传统的IO读取
     * @param oriStr
     * @param destStr
     */
    public void customRead(String oriStr,String destStr){
        FileInputStream in = null;
        FileOutputStream os = null;
        try {
            in = new FileInputStream(oriStr);
            os = new FileOutputStream(destStr);
            //一次读取1K 数据
            byte[] bytes = new byte[1024];
            while(in.read(bytes)!=-1){
              os.write(bytes);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                in.close();
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 带缓冲区的IO读取
     * @param oriStr
     * @param destStr
     */
    public void bufferRead(String oriStr,String destStr){
        BufferedInputStream in = null;
        BufferedOutputStream os = null;
        try {
            in = new BufferedInputStream(new FileInputStream(oriStr));
            os = new BufferedOutputStream(new FileOutputStream(destStr));
            //一次读取1K 数据
            byte[] bytes = new byte[1024];
            while(in.read(bytes)!=-1){
                os.write(bytes);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                in.close();
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

     QA  很多人看了上述2段代码后,可能要反驳啦,customRead有缓存啊,不是一次性读取1024个字节到byte数组了吗

     QA  bufferRead 为啥一定比customeRead 速度快

     其实要理解上面2个问题,很简单,

     ①:fileInputStream.read(byte[]) 是一个骗人的方法,他是通过循环操作调用read()方法,每次读取一个字节,然后填满byte数组。

     ②:BufferedInputStream.read(byte[]) 他是预先从磁盘读取最大8K的数据,然后保存在本地的一个叫buf 数组中,后面读取数据的时候直接从这个buf中获取

      fileInputStream 每次从磁盘获取数据,BufferedInputStream 每次才从本地缓存拿数据,一目了然,BufferedInputStream 明显快多啦(源码不是很复杂,有兴趣的朋友可以看看源码就知道啦

   

网络IO

    网络IO主要延时由: 服务器响应延时 + 带宽限制 + 网络延时 + 跳转路由延时 + 本地接收延时 决定。(一般为几十到几千毫秒,受环境干扰极大),考虑到网络IO瓶颈,就有很多传输模型,BIO,NIO等等 在这些模型中就不得不提 ByteBuffer,

    那ByteBuffer 是啥,其实这个东西经常用在网络传输NIO模型上,网络传输为了提高效率,不可能一个一个在磁盘上读取,那么需要读取一块数据存储到内存里,ByteBuffer(ByteBuffer内部维护一个byte[],是不是很熟悉,这个套路)就是应用缓存,负责在内存中保存读取的那一整块数据

//伪代码
public void nioRead() { FileChannel channel=null; try{ RandomAccessFile raf=new RandomAccessFile("src/exp_io.txt","rw"); //获取通道 channel=raf.getChannel(); if(0==raf.length()){ return; } //分配缓存空间 ByteBuffer buffer=ByteBuffer.allocate((int)raf.length()); byte [] bytes=new byte[(int)raf.length()]; //将数据写入缓存 int bytesRead=channel.read(buffer); int i=0; while (bytesRead!=-1){ buffer.flip(); while (buffer.hasRemaining()){ //从缓存读取数据 bytes[i]=buffer.get(); i++; } //将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面 //现在向缓存中写入数据时不会覆盖未读的数据 buffer.compact(); bytesRead=channel.read(buffer); } System.out.println(new String(bytes)); }catch (Exception e){ }finally { try{ if (channel!=null){ channel.close(); } }catch (Exception e){ } } }

  

总结

       java 为提高IO读写的效率,就一个办法,想办法一次性多读取数据,然后缓存起来。其实ByteBuffer 和BufferedInputStream的根本方法都是一致的,就是搞一个本地的应用缓存,来解决IO瓶颈。

       考虑一个问题 ByteBuffer 是堆内分配内存,如果,我直接用堆外内存(减少系统缓存copy到应用缓存中)作为应用缓存,那么能不能提高IO瓶颈。(netty的套路,有兴趣大家可以研究一下哈)

参考资料

      网络IO和磁盘IO详解 https://www.cnblogs.com/sunsky303/p/8962628.html 

     BufferByte用法小结 https://blog.csdn.net/mrliuzhao/article/details/89453082

posted on 2020-07-30 19:04  柠檬糖大人你尽然盗号  阅读(290)  评论(0编辑  收藏  举报

导航