zoukankan      html  css  js  c++  java
  • Java IO全面

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10857412.html

    一:IO流梳理——字符流、字节流、输入流、输出流

      见另一篇博文:https://www.cnblogs.com/ygj0930/p/5827509.html

      

    二:同步&异步、阻塞&非阻塞的概念

      同步:同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。

      异步:当一个异步过程调用发出后,调用者不能立刻得到结果,可以先做其他事。当这个调用在完成后,通过状态、通知和回调来通知调用者。

      同步与异步 是 调用与结果 的关系。

      阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起(挂起:线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。

         同步 不等于 阻塞:对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回,它还是会抢占cpu去执行其他逻辑,也会主动检测io是否准备好

      非阻塞:调用函数时,在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回,在之后通过select通知调用者。

      阻塞与非阻塞 是 调用与线程状态 的关系。

    三:Linux的5 种 IO 模型

      1)阻塞I/O(blocking I/O)

      应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待….直到数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。

      2)非阻塞I/O (nonblocking I/O)

      非阻塞IO通过进程反复调用IO函数(多次系统调用,并马上返回),而在IO过程中,进程是阻塞的。

      我们把一个SOCKET接口设置为非阻塞的意思就是:告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间。


      3) I/O复用(select 和poll) (I/O multiplexing)  

      能实现同时对多个IO端口进行监听; I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。


      4)信号驱动I/O (signal driven I/O (SIGIO))
      首先我们允许进程进行信号驱动I/O,并定义一个信号处理函数;

      当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。

      以上四种其实都是同步IO。


      5)异步I/O (asynchronous I/O (the POSIX aio_functions))

      当一个异步过程调用发出后,调用者不能立刻得到结果,它转身去做其他事情;

      被调用者在执行完毕后,通过状态、通知、回调等信息,通知调用者做完了,然后调用者再接着之前的工作往下进行。

     

    三:BIO、NIO 和 AIO 的区别

      BIO:同步并阻塞IO,一个连接一个线程。

     

     NIO:轮询,通道有请求就调用对应处理函数。

     “多路复用IO+同步非阻塞IO”,一个单线程Selector阻塞轮询,找到有数据的channel进行非阻塞IO。

     NIO是基于事件驱动模式的IO,Reactor模式是事件驱动模式的实现,主要原理见:https://www.jianshu.com/p/eef7ebe28673

     Selector可以轮询多个Channel,因为JDK使用了epoll()代替传统的select实现,没有最大连接句柄限制,所以只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端。

     应用程序向selector注册一个channel,由selector不断轮询,如果发现有某个channel发生连接请求,就会通知相应的处理线程。

     

     AIO:订阅,操作系统有IO就通知程序处理。

     AIO框架在windows下使用windows IOCP技术,在Linux下使用epoll多路复用IO技术模拟异步IO。

     应用程序向操作系统注册IO监听,然后继续做自己的事情。操作系统发生IO事件,并且准备好数据后,在主动通知应用程序,触发相应的函数。

    四:Netty框架

      1、Java原生NIO存在的问题

      1)NIO 的类库和 API 繁杂,使用麻烦:你需要熟练掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等。

      2)需要具备其他的额外技能做铺垫:例如熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的 NIO 程序。

      3)可靠性能力补齐,开发工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等。NIO 编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大。

      4)JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。官方声称在 JDK 1.6 版本的 update 18 修复了该问题,但是直到 JDK 1.7 版本该问题仍旧存在,只不过该 Bug 发生概率降低了一些而已,它并没有被根本解决。

      2、Netty适用场景

      Netty 对 JDK 自带的 NIO 的 API 进行了封装,主要使用场景如下:

      1)互联网行业:在分布式系统中,各个节点之间需要远程服务调用,高性能的 RPC 框架必不可少,Netty 作为异步高性能的通信框架,往往作为基础通信组件被这些 RPC 框架使用。

        典型的应用有:阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信,Dubbo 协议默认使用 Netty 作为基础通信组件,用于实现各进程节点之间的内部通信。

      2)大数据领域:经典的 Hadoop 的高性能通信和序列化组件 Avro 的 RPC 框架,默认采用 Netty 进行跨界点通信,它的 Netty Service 基于 Netty 框架二次封装实现。

      3、Netty的高性能设计

      Netty 是异步事件驱动的,高性能之处主要来自于其 I/O 模型和线程处理模型,前者决定如何收发数据,后者决定如何处理数据

      1)基于 I/O多路复用模式

      Netty 的非阻塞 I/O 的实现关键是基于 I/O 复用模型:

      

      2)面向Buffer的数据读写

      传统的 I/O 是面向字节流或字符流的,以流式的方式顺序地从一个 Stream 中读取一个或多个字节, 因此也就不能随意改变读取指针的位置。

      在 NIO 中,抛弃了传统的 I/O 流,而是引入了 Channel 和 Buffer 的概念L:在 NIO 中,只能从 Channel 中读取数据到 Buffer 中或将数据从 Buffer 中写入到 Channel,可以随意地读取任意位置的数据。

      3)事件驱动的线程模型

      发生事件时,主线程把事件放入事件队列。

      在另外的线程中不断循环消费事件列表中的事件,调用事件对应的处理逻辑处理事件。

      事件驱动方式也被称为消息通知方式,其实是设计模式中观察者模式的实现

      包括 4 个基本组件:

      1)事件队列(event queue):接收事件的入口,存储待处理事件;

      2)分发器(event mediator):将不同的事件分发到不同通道;

      3)事件通道(event channel):分发器与业务处理器之间的联系渠道;

      4)事件处理器(event processor):实现业务逻辑,处理完成后会发出事件,触发下一步操作。

      此模式的优点:

      1)可扩展性好:基于分布式的异步架构,事件与处理器之间高度解耦,可以方便扩展事件处理逻辑;

      2)高性能:基于队列暂存事件,能方便并行异步处理事件。

      NIO的线程模型——Reactor模型(反应堆模型)

      服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程.

      Reactor 模式也叫 Dispatcher 模式,即 I/O 多路复用统一监听事件,收到事件后分发(Dispatch 给某进程),是编写高性能网络服务器的必备技术之一。

      Reactor 模型中有 2 个关键组成:

      1)Reactor:Reactor 在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对 IO 事件做出反应。

      2)Handlers:处理程序执行 I/O 事件要完成的实际事件,Reactor 通过调度适当的处理程序来响应 I/O 事件,处理程序执行非阻塞操作。

      Netty的线程模型——基于NIO的主从 Reactors 多线程模型作进一步修改

      a)MainReactor 负责客户端的连接请求,并将请求转交给 SubReactor;

      b)SubReactor 负责相应通道的 IO 读写请求;

      c)非 IO 请求(具体逻辑处理)的任务则会直接写入队列,等待 worker threads 进行处理。

      4)基于异步的事件处理方式

      Netty 中的 I/O 操作是异步的,包括 Bind、Write、Connect 等操作会简单的返回一个 ChannelFuture。

      调用者并不能立刻获得结果,而是通过 Future-Listener 机制,用户可以方便的主动获取或者通过通知机制获得 IO 操作结果

      当 Future 对象刚刚创建时,处于非完成状态,调用者可以通过返回的 ChannelFuture 来获取操作执行的状态,注册监听函数来执行完成后的操作。

      

      

      

      

  • 相关阅读:
    移动端rem.js及移动端的一些属性和事件
    win10下nodejs的安装及配置
    $.ajax()在IE9下的兼容性问题
    H5 video自定义视频控件
    js表格排序
    [转]jQuery不同版本区别
    移动端H5多终端适配解决方案
    一个目录结构
    Linux学习
    Python3基础笔记---线程与进程
  • 原文地址:https://www.cnblogs.com/ygj0930/p/10857412.html
Copyright © 2011-2022 走看看