热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

1前言我们都知道,为了实现高性能的通信服务器,BIO在高并发的情况下会出现性能急剧下降的问题,甚至会由于创建过多线程而导致系统OOM。因此在Java业界,BIO的性能问题一直被开发

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


4 总结

好了,五种IO模型基本就已经总结完了,基本是自己基于《UNIX网络编程_卷1_套接字》的读书总结


原文链接:https://mp.weixin.qq.com/s?__biz=MzAwMDczMjMwOQ==&mid=2247484159&idx=1&sn=62a1626a748547ad837f9c6ccc10b11a


如果觉得本文对你有帮助,可以关注一下我公众号,回复关键字【面试】即可得到一份Java核心知识点整理与一份面试大礼包!另有更多技术干货文章以及相关资料共享,大家一起学习进步!



推荐阅读
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • Java高并发与多线程(二):线程的实现方式详解
    本文将深入探讨Java中线程的三种主要实现方式,包括继承Thread类、实现Runnable接口和实现Callable接口,并分析它们之间的异同及其应用场景。 ... [详细]
  • 深入解析CAS机制:全面替代传统锁的底层原理与应用
    本文深入探讨了CAS(Compare-and-Swap)机制,分析了其作为传统锁的替代方案在并发控制中的优势与原理。CAS通过原子操作确保数据的一致性,避免了传统锁带来的性能瓶颈和死锁问题。文章详细解析了CAS的工作机制,并结合实际应用场景,展示了其在高并发环境下的高效性和可靠性。 ... [详细]
  • 本文介绍了如何利用 Delphi 中的 IdTCPServer 和 IdTCPClient 控件实现高效的文件传输。这些控件在默认情况下采用阻塞模式,并且服务器端已经集成了多线程处理,能够支持任意大小的文件传输,无需担心数据包大小的限制。与传统的 ClientSocket 相比,Indy 控件提供了更为简洁和可靠的解决方案,特别适用于开发高性能的网络文件传输应用程序。 ... [详细]
  • 小王详解:内部网络中最易理解的NAT原理剖析,挑战你的认知极限
    小王详解:内部网络中最易理解的NAT原理剖析,挑战你的认知极限 ... [详细]
  • 本文深入解析了Java 8并发编程中的`AtomicInteger`类,详细探讨了其源码实现和应用场景。`AtomicInteger`通过硬件级别的原子操作,确保了整型变量在多线程环境下的安全性和高效性,避免了传统加锁方式带来的性能开销。文章不仅剖析了`AtomicInteger`的内部机制,还结合实际案例展示了其在并发编程中的优势和使用技巧。 ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 思科IOS XE与ISE集成实现TACACS认证配置
    本文详细介绍了如何在思科IOS XE设备上配置TACACS认证,并通过ISE(Identity Services Engine)进行用户管理和授权。配置包括网络拓扑、设备设置和ISE端的具体步骤。 ... [详细]
  • 本文总结了在SQL Server数据库中编写和优化存储过程的经验和技巧,旨在帮助数据库开发人员提升存储过程的性能和可维护性。 ... [详细]
  • 在什么情况下MySQL的可重复读隔离级别会导致幻读现象? ... [详细]
  • 本文是Java并发编程系列的开篇之作,将详细解析Java 1.5及以上版本中提供的并发工具。文章假设读者已经具备同步和易失性关键字的基本知识,重点介绍信号量机制的内部工作原理及其在实际开发中的应用。 ... [详细]
  • 深入解析 Synchronized 锁的升级机制及其在并发编程中的应用
    深入解析 Synchronized 锁的升级机制及其在并发编程中的应用 ... [详细]
  • 深入浅析JVM垃圾回收机制与收集器概述
    本文基于《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》的阅读心得进行整理,详细探讨了JVM的垃圾回收机制及其各类收集器的特点与应用场景。通过分析不同垃圾收集器的工作原理和性能表现,帮助读者深入了解JVM内存管理的核心技术,为优化Java应用程序提供实用指导。 ... [详细]
author-avatar
云姵肇
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有