一、五种IO模型

在《Unix网络编程》一书中提到了五种IO模型,分别是:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO。

五种IO模型

1.1 阻塞IO模型

最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。

当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态。

过程如下:

1.2 非阻塞IO模型

当用户线程发起一个read操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。

所以事实上,在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU。

1.3 多路复用IO模型

多路复用IO模型是目前使用得比较多的模型。Java NIO实际上就是多路复用IO。

在多路复用IO模型中,会有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。

1.4 信号驱动IO模型

在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。

1.5 异步IO模型

异步IO模型才是最理想的IO模型,在异步IO模型中,当用户线程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它受到一个asynchronous read之后,它会立刻返回,说明read请求已经成功发起了,因此不会对用户线程产生任何block。然后,内核会等待数据准备完成,然后将数据拷贝到用户线程,当这一切都完成之后,内核会给用户线程发送一个信号,告诉它read操作完成了。也就说用户线程完全不需要实际的整个IO操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示IO操作已经完成,可以直接去使用数据了。

二、NIO,BIO,AIO选型

2.1同步阻塞IO(BIO)

我们熟知的Socket编程就是BIO,一个socket连接一个处理线程(这个线程负责这个Socket连接的一系列数据传输操作)。

阻塞的原因在于:操作系统允许的线程数量是有限的,多个socket申请与服务端建立连接时,服务端不能提供相应数量的处理线程,没有分配到处理线程的连接就会阻塞等待或被拒绝。

缺点:

1、IO代码里read操作是阻塞操作,如果连接不做数据读写操作会导致线程阻塞,浪费资源
2、如果线程很多,会导致服务器线程太多,压力太大,比如C10K问题

应用场景:

BIO 方式适用于连接数目比较小且固定的架构, 这种方式对服务器资源要求比较高, 但程序简单易理解。

2.2 同步非阻塞IO(NIO)

New IO是对BIO的改进,基于Reactor模型。

同步非阻塞,服务器实现模式为一个线程可以处理多个请求(连接),客户端发送的连接请求都会注册到多路复用器selector上,多路复用器轮询到连接有IO请求就进行处理,JDK1.4开始引入。

应用场景:

NIO方式适用于连接数目多且连接比较短(轻操作) 的架构, 比如聊天服务器, 弹幕系统, 服务器间通讯,编程比较复杂。

2.3 异步非阻塞 I/O(AIO)

最后,异步非阻塞 I/O 模型是一种处理与 I/O 重叠进行的模型。读请求会立即返回,说明 read 请求已经成功发起了。

在后台完成读操作时,应用程序然后会执行其他处理操作。当 read 的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次 I/O 处理过程。

AIO是对NIO的改进(所以AIO又叫NIO.2),它是基于Proactor模型的。

一般适用于连接数较多且连接时间较长的应用。

Proactor模型原理待续?

AIO与NIO的区别:AIO是发出IO请求后,由操作系统自己去获取IO权限并进行IO操作;NIO则是发出IO请求后,由线程不断尝试获取IO权限,获取到后通知应用程序自己进行IO操作。

各种I/O的对比

三、同步非阻塞IO(NIO)

3.1 Reactor模式

在Reactor模式中,会先对每个client注册感兴趣的事件,容器可以是一个hashmap。key为clientID,value为事件消息集合。

在nio中是注册在选择器里面的,然后有一个线程专门去轮询每个client是否有事件发生,当有事件发生时,便顺序处理每个事件,当所有事件处理完之后,便再转去继续轮询,如下图所示:


注意,上面的图中展示的 是顺序处理每个事件,当然为了提高事件处理速度,可以通过多线程或者线程池的方式来处理事件,即可以使用线程池来处理业务逻辑,以提高轮询线程的执行效率。流程则变为检查队列的线程如果发现了需要执行的事件,则就将它提交线程池里处理,然后继续轮训。

3.2 java NIO和IO的主要区别

下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部分的差异。

IO NIO
面向流 面向缓冲
阻塞IO 非阻塞IO
选择器

1、面向流与面向缓冲

Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。

Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。

Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

即:一个流中读写数据,一个从缓冲读写数据

2、阻塞与非阻塞IO
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。

Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

3、选择器(Selectors)

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以在一个选择器中注册多个通道,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

3.2 NIO的核心组件

NIO 有三大核心组件: Channel(通道), Buffer(缓冲区),Selector(多路复用器)

1、channel 类似于流,每个 channel 对应一个 buffer缓冲区,buffer 底层就是个数组

2、channel 会注册到 selector 上,由 selector 根据 channel 读写事件的发生将其交由某个空闲的线程处理

3、NIO 的 Buffer 和 channel 都是既可以读也可以写

3.2.1 Channel

首先说一下Channel,国内大多翻译成“通道”。Channel和IO中的Stream(流)是差不多一个等级的。只不过Stream是单向的,譬如:InputStream, OutputStream.而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。

NIO中的Channel的主要实现有:

FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel

3.3 nio处理网路请求实例

nio组件之间的工作流程如下

1.创建ServerSocketChannel并绑定端口

2.创建Selector多路复用器,并注册Channel

3.循环监听是否有感兴趣的事件发生selector.select();

4.获得事件的句柄,并进行处理

NIO底层在JDK1.4版本是用linux的内核函数select()或poll()来实现,跟上面的NioServer代码类似,selector每次都会轮询所有的sockchannel看下哪个channel有读写事件,有的话就处理,没有就继续遍历,JDK1.5开始引入了epoll基于事件响应机制来优化NIO。

NIO整个调用流程就是Java调用了操作系统的内核函数来创建Socket,获取到Socket的文件描述符,再创建一个Selector对象,对应操作系统的Epoll描述符,将获取到的Socket连接的文件描述符的事件绑定到Selector对应的Epoll文件描述符上,进行事件的异步通知,这样就实现了使用一条线程,并且不需要太多的无效的遍历,将事件处理交给了操作系统内核(操作系统中断程序实现),大大提高了效率。

3.4 Redis线程模型

Redis就是典型的基于epoll的NIO线程模型(nginx也是),epoll实例收集所有事件(连接与读写事件),由一个服务端线程连续处理所有事件命令。
Redis底层关于epoll的源码实现在redis的src源码目录的ae_epoll.c文件里,感兴趣可以自行研究。

思考:

1. select,poll,epoll 的区别

I/O多路复用底层主要用的Linux 内核·函数(select,poll,epoll)来实现,windows不支持epoll实现,windows底层是基于winsock2的select函数实现的(不开源)

select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。select,poll原理相似都是一个线程去线性遍历,而epoll时事件通知方式。


epoll可以理解为Java设计模式中的观察者模式中的push模式。

2.为什么Netty使用NIO而不是AIO?

在Linux系统上,AIO的底层实现仍使用Epoll,没有很好实现AIO,因此在性能上没有明显的优势,而且被JDK封装了一层不容易深度优化,Linux上AIO还不够成熟。

Netty是异步非阻塞框架,Netty在NIO上做了很多异步的封装。

参考资料

1.Java NIO:浅析I/O模型https://www.cnblogs.com/dolphin0520/p/3916526.html

2.BIO、NIO和AIO的区别(简明版)https://www.cnblogs.com/ygj0930/p/6543960.html

3.Java NIO:IO与NIO的区别 https://www.cnblogs.com/xiaoxi/p/6576588.html

4.漫谈Java IO之 NIO那些事儿 https://www.cnblogs.com/xing901022/p/8672418.html

5.攻破JAVA NIO技术壁垒 https://blog.csdn.net/u013256816/article/details/51457215#comments

NIO核心设计与原理相关推荐

  1. Kafka核心设计与实践原理总结:进阶篇

    作者:未完成交响曲,资深Java工程师!目前在某一线互联网公司任职,架构师社区合伙人! kafka作为当前热门的分布式消息队列,具有高性能.持久化.多副本备份.横向扩展能力.我学习了<深入理解K ...

  2. Kafka核心设计与实践原理总结:基础篇

    作者:未完成交响曲,资深Java工程师!目前在某一线互联网公司任职,架构师社区合伙人! 一.基本概念 1.体系架构 Producer:生产者 Consumber:消费者 Broker:服务代理节点(k ...

  3. 《深入理解Kafka:核心设计与实践原理》笔误及改进记录

    2019年2月下旬笔者的有一本新书--<深入理解Kafka:核心设计与实践原理>上架,延续上一本<RabbitMQ实战指南>的惯例,本篇博文用来记录现在发现的一些笔误,一是给购 ...

  4. 新书《深入理解Kafka:核心设计与实践原理》上架,感谢支持~

    新书上架 初识 Kafka 时,笔者接触的还是 0.8.1 版本,Kafka 发展到目前的 2.x 版本,笔者也见证了Kafka的蜕变,比如旧版客户端的淘汰.新版客户端的设计.Kafka 控制器的迭代 ...

  5. 大数据Hadoop(五)HDFS的原理之核心设计(1)

    大家好,我是脚丫先生 (o^^o) 前面介绍了HDFS的API操作,那么接下来就对HDFS原理之核心设计与工作机制进行详细的分析. 好了,我们开始今天的正文. 文章目录 一.HDFS心跳机制 二.HD ...

  6. 图解 DataX 核心设计原理

    DataX 是阿里巴巴开源的一个异构数据源离线同步工具,致力于实现包括关系型数据库(MySQL.Oracle 等).HDFS.Hive.ODPS.HBase.FTP 等各种异构数据源之间稳定高效的数据 ...

  7. 深入理解Kafka核心设计与实践原理_01

    深入理解Kafka核心设计与实践原理_01 01_初识Kafka 1.1 基本概念 1.2 安装与配置 1.3 生产与消费 1.4 服务端参数配置 01_初识Kafka 1.1 基本概念 一个典型的 ...

  8. 【转】NIO的定义和原理是什么?

    NIO和IO到底有什么区别?有什么关系? 首先说一下核心区别: NIO是以块的方式处理数据,但是IO是以最基础的字节流的形式去写入和读出的.所以在效率上的话,肯定是NIO效率比IO效率会高出很多. N ...

  9. NIO 之 Channel实现原理

    相关文章 IO.NIO.AIO 内部原理分析 NIO 之 Selector实现原理 NIO 之 ByteBuffer实现原理 NIO概述 Java NIO 由以下几个核心部分组成: Channels ...

最新文章

  1. Objective-C中的一些特殊的数据类型
  2. 小马智行L4无人车今日量产下线,这比融资更具里程碑意义
  3. 银行家算法的分析与实现
  4. Android自定义AlertDialog
  5. mysql将sql转为关系代数_关系数据库基础:关系代数运算知识笔记
  6. 2075. 解码斜向换位密码
  7. 每日一题(38)—— 一个32位的机器,该机器的指针是多少位?
  8. cassandra可视化工具_一位数据科学家的私房工具清单
  9. 如何在 Adobe XD 中创建交互式原型?
  10. mysql12260怎么关闭_关于mysql的启动报错处理
  11. USE_DB_RECOVERY_FILE_DEST的使用详解(转载)
  12. SAP 各模块常用T-Code
  13. 通过IP地址获取地理位置信息
  14. mongo按季度统计_三季度国内纯碱市场总结及四季度分析
  15. 微信的那个老外产品经理,又写了一篇《中国移动应用设计趋势》
  16. 与朋友谈心,也是与自己谈心
  17. 【django】如何获取字段最大值,最新的记录
  18. STC8单片机ADC采样注意事项
  19. URL缩短器:详细说明
  20. HART475艾默生手操器维修注意事项及指标特点

热门文章

  1. 眼镜选款新方法,用AR+Scene技术实现3D虚拟试戴
  2. MP4 全介绍【转载】
  3. 植物大战僵尸:代码实现自动收集阳光
  4. 在html5中flex布局详解,Flex布局详解(一)
  5. 1刷黑群晖_当苹果用户拥有NAS后可以做什么?群晖DS220J体验测评
  6. 第十篇 -- Windows 下免费的GIF录制工具
  7. 电气专业c语言要学得非常好吗,电气自动化专业需要学C语言吗?
  8. 一元二次方程解法(待更新……)
  9. net share c$=c: 发生系统错误
  10. 华为路由器 上网慢 延迟高 解决方法