当前位置:首页 > > 架构师社区
[导读]我们都知道,为了实现高性能的通信服务器,BIO在高并发的情况下会出现性能急剧下降的问题,甚至会由于创建过多线程而导致系统OOM。

1 前言

我们都知道,为了实现高性能的通信服务器,BIO在高并发的情况下会出现性能急剧下降的问题,甚至会由于创建过多线程而导致系统OOM。因此在Java业界,BIO的性能问题一直被开发者所诟病,所幸的是,JDK1.4推出了NIO,NIO基本解决了BIO的性能问题,是目前实现Java高性能服务器的基础框架。NIO官方的叫法叫做New IO,而对应于操作系统层面来说其实也是Non-Blocking IO。

大名鼎鼎的Netty就是NIO框架,而目前很多开源框架比如Dubbo,RocketMQ,Seata,Spark,Flink都是采用Netty作为基础通信组件。因此,学好Netty很重要,但是NIO作为Netty的基础,这里想说的是学好NIO也一样重要!

学好NIO,那么必须先理解操作系统层面的5种网络IO模型。

2 5种IO模型

2.1 阻塞IO模型

阻塞IO模型如下图:

大白话详解5种网络IO模型

从上图可以看到,不管有无数据报到来,进程(线程)是阻塞于recvfrom系统调用的。这是什么意思呢?说白了就是假如我们要用套接字读取数据,此时我们必然会调用read方法,此时这个read方法就会触发操作系统内核的一次recvfrom系统调用,此时有两种情况:

  1. 内核还未接收到远端数据,此时数据报没有准备好,那么读取数据的线程就会一直阻塞,直到远端发来数据报,这一阻塞的过程对应上图序号1的过程;然后在数据报被从内核复制到用户空间这一过程中,该线程会再次阻塞,直到复制完成,这一过程对应上图的序号2的过程;
  2. 内核已经接收到远端数据,此时数据报已经准备好,那么数据报就会被从内核复制到用户空间,这一过程是阻塞的,对应上图序号2的过程。

可见,阻塞IO模型的话,读一次数据会发生一次recvfrom系统调用,整个过程都是阻塞的,即在内核的数据报还未准备好的时候,此时用户进程( 线程)阻塞;当内核的数据报准备好的时候,此时数据报要从内核拷贝到用户空间,此时用户进程(线程)也一直阻塞;直到数据报拷贝到用户空间后,此时用户进程(线程)才会醒过来,然后处理这些数据报即执行一些用户的业务逻辑。当然,如果用户进程(线程)在阻塞过程中,如果recvfrom系统调用被信号中断,此时阻塞也是会被唤醒的。

思考: 这里的recvfrom系统调用被信号中断什么情况下会发生?这个信号中断指的是线程中断(Thread.interrupt())么?自行思考。

2.2 非阻塞IO模型

非阻塞IO模型如下图:

大白话详解5种网络IO模型

如上图,根据内核中的数据报有无准备好,有以下两种情形:

  1. 当内核中的数据报还没准备好,此时 recvfrom系统调用立即返回一个 EWOULDBLOCK错误,即不会将用户进程(线程)至于阻塞状态。我们拿Java的NIO来说,当我们配置 ServerSocketChannel.configureBlocking(false);或 SocketChannel..configureBlocking(false);时,我们调用 ServerSocketChannel.accept()的 null或 SocketChannel.read(buffer)不会阻塞的,若没有新连接接入或内核中没有数据报准备好,此时会理解返回 null或 0的返回结果,说白了这个返回结果就是对应 EWOULDBLOCK错误;
  2. 当内核中的数据报已经准备好时,此时 recvfrom系统调用,用户进程(线程)还是会阻塞,直到内核中的数据报已经拷贝到了用户空间,此时用户进程(线程)才会被唤醒来处理接收的数据报。

非阻塞IO在用户数据报还没准备好的时候,recvfrom系统调用不会阻塞,接着会继续进行下一轮的recvfrom系统调用看数据报有无准备好,周而复始,进程(线程)不断轮训,因此这是非常耗费CPU的。这种模型不是很常用,适合用在某台CPU专为某些功能准备的场合。

2.3 IO复用模型

IO复用模型如下图:

大白话详解5种网络IO模型

初步从以上IO复用模型来看,这不是跟IO阻塞模型差不多么?当内核无数据报准备好时,select系统调用会阻塞;当内核数据拷贝到用户空间时,此时recvfrom系统调用依然会阻塞,实在是看不到跟IO阻塞模型有啥区别?区别就是IO复用模型还比阻塞IO模型还多一次recvfrom系统调用,这不是明摆着多浪费一次CPU资源么?

如果我们这么想,那为什么IO复用模型得到大规模广泛应用呢?其实IO复用模型真正占优势的地方在于select操作,这个select操作可以选择多个文件描述符,分别对应Java NIO中的OP_CONNECT,OP_ACCEPT,OP_READ和OP_WRITE就绪事件。正是基于一次recvfrom系统调用中一个线程的select操作可以选择多个文件描述符这个功能,我们现在用一个用户线程就能监听不同channel的OP_CONNECT,OP_ACCEPT,OP_READ和OP_WRITE这些就绪事件,然后根据某个就绪事件拿到相应的channel来做对应的操作。而不用像阻塞IO模型或非阻塞IO模型那样,一次recvfrom系统调用中一个线程就只能选择一个文件描述符,这样就严重限制了伸缩性。这么说很抽象,就比如拿阻塞IO模型来说,由于用户进程(线程)每一次recvfrom系统调用都是阻塞且只对应一个文件描述符,此时如果服务端线程阻塞于客户端A的读操作时,如果有另外的客户端B需要接入服务端,此时服务端线程由于阻塞于客户端A的读操作,因此无法处理客户端B的连接操作。此时,必然要一个线程一个文件描述符即服务端线程每accept了一个客户端连接,此时就需要新建一个线程去处理这个客户端连接的读写操作。我们都知道,线程是一种很昂贵的CPU资源,当开启成千上万的线程后,线程切换的成本很高,CPU性能肯定下降,说不定高并发下还会OOM。说到这里,也许有同学会说,对于阻塞IO模型,我们不一个线程一个socket,用线程池替代,当然,这是一个优化的点,但没解决阻塞IO模型的根本。怎么说呢?当线程池的所有线程都阻塞于客户端的读或写操作时,此时其他新接入的线程将会积压在线程池的队列中阻塞等待。

2.4 信号驱动IO模型

信号驱动IO模型如下图:

大白话详解5种网络IO模型

可见,信号驱动IO模型在等待数据报期间是不会阻塞的,即用户进程(线程)发送一个sigaction系统调用后,此时立刻返回,并不会阻塞,然后用户进程(线程)继续执行;当数据报准备好时,此时内核就为该进程(线程)产生一个SIGIO信号,此时该进程(线程)就发生一次recvfrom系统调用将数据报从内核复制到用户空间,注意,这个阶段是阻塞的。

PS: 网上找了下信号驱动IO模型的java代码,没找到,会码信号驱动IO模型代码的下伙伴们可以教教我。

2.5 异步IO模型

异步IO模型如下图:

大白话详解5种网络IO模型

异步IO模型也很好理解,即用户进程(线程)在等待数据报和数据报从内核拷贝到用户空间这两阶段都是非阻塞的,即用户进程(线程)发生一次系统调用后,立即返回,然后该用户进程(线程)继续往下执行。当内核把接收到数据报并把数据报拷贝到了用户空间后,此时再通知用户进程(线程)来处理用户空间的数据报。也就是说,这一些列IO操作都交给了内核去处理了,用户进程无须同步阻塞,因此是异步非阻塞的。

扩展: 异步IO模型跟信号驱动IO模型的区别在于当内核准备好数据报后,对于信号驱动IO模型,此时内核会通知用户进程说数据报准备好啦,你需要发起系统调用来将数据报从内核拷贝到用户空间,此过程是同步阻塞的;而对于异步IO模型,当数据报准备好时,内核不会再通知用户进程,而是自己默默将数据报从内核拷贝到用户空间后然后再通知用户进程说,数据已经拷贝到用户空间啦,你直接进行业务逻辑处理就行。

3 各种IO模型区别

大白话详解5种网络IO模型

通过5种IO模型的比对,可以发现,前4IO模型都是同步阻塞IO模型,因为其第二阶段数据报从内核拷贝到用户空间都是同步阻塞的,只是第一阶段等待数据报的处理不同;最后一种IO模型(异步IO模型)才是真正的异步非阻塞IO模型,内核将一切事情都干完(内核:我真的好累)。

4 总结

好了,五种IO模型基本就已经总结完了,基本是自己基于《UNIX网络编程_卷1_套接字》的读书总结,接下来再通过java代码将这几种IO模型实现一遍。

参考:《UNIX网络编程_卷1_套接字》


免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭