Java IO全面

 

转载请注明原文地址:http://www.javashuo.com/article/p-qivyngym-mv.htmlhtml

 

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

  见另外一篇博文:http://www.javashuo.com/article/p-gmyidsnn-bu.html编程

  

 

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

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

  异步:当一个异步过程调用发出后,调用者不能马上获得结果,能够先作其余事。当这个调用在完成后,经过状态、通知和回调来通知调用者。设计模式

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

 

  阻塞:阻塞调用是指调用结果返回以前,当前线程会被挂起(挂起:线程进入非可执行状态,在这个状态下,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框架

  一、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 发生几率下降了一些而已,它并无被根本解决。

 

  二、Netty适用场景

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

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

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

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

 

  三、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 来获取操做执行的状态,注册监听函数来执行完成后的操做。

相关文章
相关标签/搜索