系统间通信4:基本IO通信模型

2023-05-16

本文引用 : https://blog.csdn.net/yinwenjie/article/details/48472237

目前常用的IO通信模型包括四种:阻塞式同步IO、非阻塞式同步IO、多路复用IO和真正的异步IO。所有IO模式都是要靠操作系统进行支持,应用程序只是提供相应的实现,对操作系统进行调用。

1. 传统阻塞模式(BIO)

BIO就是:blocking IO。最容易理解、最容易实现的IO工作方式,**应用程序向操作系统请求网络IO操作,这时应用程序会一直等待;另一方面,操作系统收到请求后,也会等待,直到网络上有数据传到监听端口;操作系统在收集数据后,会把数据发送给应用程序;最后应用程序受到数据,并解除等待状态。**如下图所示:
在这里插入图片描述
注意:上图中交互的两个元素是应用程序和它所使用的操作系统
就TCP协议来说,整个过程实际上分成三个步骤:三次握手建立连接、传输数据(包括验证和重发)、断开连接。

BIO存在的问题:

  • 同一时间,服务器只能接受来自于客户端A的请求信息;虽然客户端A和客户端B的请求是同时进行的,但客户端B发送的请求信息只能等到服务器接受完A的请求数据后,才能被接受。
  • 由于服务器一次只能处理一个客户端请求,当处理完成并返回后(或者异常时),才能进行第二次请求的处理。很显然,这样的处理方式在高并发的情况下,是不能采用的。
  • 实际上以上的问题是可以通过多线程来解决的,实际上就是当accept接收到一个客户端的连接后,服务器端启动一个新的线程,来读写客户端的数据,并完成相应的业务处理。但是你无法影响操作系统底层的“同步IO”机制。

2. 非阻塞模式(NIO)

一定要注意:阻塞/非阻塞的描述是针对应用程序中的线程进行的,对于阻塞方式的一种改进是应用程序将其“一直等待”的状态主动打开,如下图所示:
在这里插入图片描述
这种模式下,应用程序的线程不再一直等待操作系统的IO状态,而是在等待一段时间后,就解除阻塞。
引入了多线程技术后,IO的处理吞吐量大大提高了,但是这样做就真的没有问题了吗,您要知道操作系统可是有“最大线程”限制的:

  • 虽然在服务器端,请求的处理交给了一个独立线程进行,但是操作系统通知accept()的方式还是单个处理的(甚至都不是非阻塞模式)。也就是说,实际上是服务器接收到数据报文后的“业务处理过程”可以多线程(包括可以是非阻塞模式),但是数据报文的接受还是需要一个一个的来。
  • 在linux系统中,可以创建的线程是有限的。我们可以通过cat /proc/sys/kernel/threads-max 命令查看可以创建的最大线程数。当然这个值是可以更改的,但是线程越多,CPU切换所需的时间也就越长,用来处理真正业务的需求也就越少
  • 创建一个线程是有较大的资源消耗的。JVM创建一个线程的时候,即使这个线程不做任何的工作,JVM都会分配一个堆栈空间。这个空间的大小默认为128K,您可以通过-Xss参数进行调整。
  • 可以使用ThreadPoolExecutor线程池来缓解线程的创建问题,但是又会造成BlockingQueue积压任务的持续增加同样消耗了大量资源。另外,如果您的应用程序大量使用长连接的话,线程是不会关闭的。这样系统资源的消耗更容易失控。

最后,无论您是使用的多线程、还是加入了非阻塞模式,这都是在应用程序层面的处理,而底层socketServer所匹配的操作系统的IO模型始终是“同步IO”,最根本的问题并没有解决。

那么,如果你真想单纯使用线程来解决问题,那么您自己都可以计算出来您一个服务器节点可以一次接受多大的并发了。看来,单纯使用线程解决这个问题不是最好的办法。

3. 多路复用IO(IO Multiplex)

目前流程的多路复用IO实现主要包括四种:select、poll、epoll、kqueue。下表是他们的一些重要特性的比较:
在这里插入图片描述
多路复用IO技术最适用的是“高并发”场景,所谓高并发是指1毫秒内至少同时有上千个连接请求准备好 QPS~100W。其他情况下多路复用IO技术发挥不出来它的优势。
在这里插入图片描述

3.1 重要概念:Channel

通道,被建立的一个应用程序和操作系统交互事件、传递内容的渠道(注意是连接到操作系统)。一个通道会有一个专属的文件状态描述符。那么既然是和操作系统进行内容的传递,那么说明应用程序可以通过通道读取数据,也可以通过通道向操作系统写数据。

3.2 重要概念:Buffer

数据缓存区:在JAVA NIO 框架中,为了保证每个通道的数据读写速度JAVA NIO 框架为每一种需要支持数据读写的通道集成了Buffer的支持。

这句话怎么理解呢?例如ServerSocketChannel通道它只支持对OP_ACCEPT事件的监听,所以它是不能直接进行网络数据内容的读写的。所以ServerSocketChannel是没有集成Buffer的。

Buffer有两种工作模式:写模式和读模式。在读模式下,应用程序只能从Buffer中读取数据,不能进行写操作。但是在写模式下,应用程序是可以进行读操作的,这就表示可能会出现脏读的情况。所以一旦您决定要从Buffer中读取数据,一定要将Buffer的状态改为读模式。

3.3 重要概念:Selector

Selector的英文含义是“选择器”,不过根据我们详细介绍的Selector的岗位职责,您可以把它称之为“轮询代理器”、“事件订阅器”、“channel容器管理机”都行。

  • 事件订阅和Channel管理:
    应用程序将向Selector对象注册需要它关注的Channel,以及具体的某一个Channel会对哪些IO事件感兴趣。Selector中也会维护一个“已经注册的Channel”的容器。
  • 轮询代理:
    应用层不再通过阻塞模式或者非阻塞模式直接询问操作系统“事件有没有发生”,而是由Selector代其询问。
  • 实现不同操作系统的支持:
    之前已经提到过,多路复用IO技术 是需要操作系统进行支持的,其特点就是操作系统可以同时扫描同一个端口上不同网络连接的时间。所以作为上层的JVM,必须要为不同操作系统的多路复用IO实现编写不同的代码。

通过上文的描述,我们知道了多路复用IO技术是操作系统的内核实现。在不同的操作系统,甚至同一系列操作系统的版本中所实现的多路复用IO技术都是不一样的。那么作为跨平台的JAVA JVM来说如何适应多种多样的多路复用IO技术实现呢?面向对象的威力就显现出来了:无论使用哪种实现方式,他们都会有“选择器”、“通道”、“缓存”这几个操作要素,那么可以为不同的多路复用IO技术创建一个统一的抽象组,并且为不同的操作系统进行具体的实现。JAVA NIO中对各种多路复用IO的支持,主要的基础是java.nio.channels.spi.SelectorProvider抽象类,其中的几个主要抽象方法包括:

  • public abstract DatagramChannel openDatagramChannel():创建和这个操作系统匹配的UDP 通道实现。

  • public abstract AbstractSelector openSelector():创建和这个操作系统匹配的NIO选择器,就像上文所述,不同的操作系统,不同的版本所默认支持的NIO模型是不一样的。

  • public abstract ServerSocketChannel openServerSocketChannel():创建和这个NIO模型匹配的服务器端通道。

  • public abstract SocketChannel openSocketChannel():创建和这个NIO模型匹配的TCP Socket套接字通道(用来反映客户端的TCP连接)

多路复用IO的优缺点:

  • 不用再使用多线程来进行IO处理了(包括操作系统内核IO管理模块和应用程序进程而言)。当然实际业务的处理中,应用程序进程还是可以引入线程池技术的
  • 同一个端口可以处理多种协议,例如,使用ServerSocketChannel测测的服务器端口监听,既可以处理TCP协议又可以处理UDP协议。
  • 操作系统级别的优化:多路复用IO技术可以是操作系统级别在一个端口上能够同时接受多个客户端的IO事件。同时具有之前我们讲到的阻塞式同步IO和非阻塞式同步IO的所有特点。Selector的一部分作用更相当于“轮询代理器”。
  • 都是同步IO:目前我们介绍的 阻塞式IO、非阻塞式IO甚至包括多路复用IO,这些都是基于操作系统级别对“同步IO”的实现。

我们一直在说“同步IO”,一直都没有详细说,什么叫做“同步IO”。实际上一句话就可以说清楚:只有上层(包括上层的某种代理机制)系统询问我是否有某个事件发生了,否则我不会主动告诉上层系统事件发生了。很明显,这里是可以继续优化的。

4. 异步IO(真正的NIO,AIO)

上述阻塞式同步IO、非阻塞式同步IO、多路复用IO 说明了IO模型是由操作系统提供支持,且这三种IO模型都是同步IO,都是采用的“应用程序不询问我,我绝不会主动通知”的方式。
异步IO则是采用“订阅-通知”模式:即应用程序向操作系统注册IO监听,然后继续做自己的事情。当操作系统发生IO事件,并且准备好数据后,在主动通知应用程序,触发相应的函数
在这里插入图片描述
和同步IO一样,异步IO也是由操作系统进行支持的。微软的windows系统提供了一种异步IO技术:IOCP(I/O Completion Port,I/O完成端口);Linux下由于没有这种异步IO技术,可以使用的是epoll对异步IO进行模拟。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

系统间通信4:基本IO通信模型 的相关文章

随机推荐

  • Bert演变总结

  • Tof,结构光,三角测距,RGBD,双目,激光雷达,毫米波雷达一文总结(一)

    Tof xff0c 结构光 xff0c 三角测距 xff0c RGBD xff0c 双目 xff0c 激光雷达 xff0c 毫米波雷达一文总结 距离测量算法解析TOF 飞行时间测距法超声波毫米波雷达激光雷达 最近在做一些无人车相关的工作 x
  • OCR-PIL.Image与Base64 String的互相转换

    1 基本环境 py2 python2 7 13py3 python3 6 2PIL pip 2 3 install pillow PIL库已不再维护 xff0c 而pillow是PIL的一个分支 xff0c 如今已超越PIL 2 Conve
  • Java中恒等条件判断:“equals”和“==”

    1 起因 xff1a 字符串恒等判断 String is reference type String str1 61 new String 34 hello 34 String str2 61 new String 34 hello 34
  • SQL小结

    1 SQL模糊查询 like 效率低 xff0c 容易全盘扫描 查找Name中包含字符 39 M 39 的数据 select ename from table where ename like 39 M 39 查找Name中第二个字母为 3
  • golang中的flag模块小结

    1 flag常用函数 无论是c语言还是golang语言或是其他语言 xff0c 启动应用程序时都可以带一些参数 xff0c 然后系统根据传入的参数进行特点的工作 如 xff1a main mode online model bert ch
  • Redis批量操作详解及性能分析

    通过mget批量执行指令可以节约网络连接和数据传输开销 xff0c 在高并发场景下可以节约大量系统资源 本文中 xff0c 我们更进一步 xff0c 比较一下redis提供的几种批量执行指令的性能 1 为什么需要批量执行redis指令 众所
  • NDCG:推荐系统/搜索评价指标

    本文转载自 胖喵 博主 xff0c 详细请看https www cnblogs com by dream p 9403984 html 1 CG xff1a 累计增益 CG xff0c cumulative gain xff0c 只考虑到了
  • 特征共线性问题

    多重共线性是使用线性回归算法时经常要面对的一个问题 在其他算法中 xff0c 例如决策树或者朴素贝叶斯 xff0c 前者的建模过程时逐渐递进 xff0c 每次都只有一个变量参与 xff0c 这种机制含有抗多重共线性干扰的功能 xff1b 后
  • 常见回归和分类损失函数比较

    文章转自知乎作者wdmad xff0c 更多内容建议阅读原文 xff1a https zhuanlan zhihu com p 36431289 本博文属于阅读笔记 xff0c 融合了个人观点 1 损失函数 损失函数的一般表示为 L y f
  • 获取keras中间层输出、模型保存与加载

    1 获取keras中间层输出 model summary and plot import keras from keras models import Model from keras utils import plot model Doc
  • HashMap底层实现和原理

    本文是在阅读知乎老刘作品后的整理 内容基于JDK1 7进行分析 xff0c 1 8做的改动文章末尾进行讲解 1 基本要义 1 1 概述 Hashmap在Map派生中的位置 HashMap基于Map接口实现 xff0c 元素以键值对的方式存储
  • 大疆激光雷达Livox Avia开箱及测试

    大疆激光雷达Livox Avia 箱子 从左至右为 xff1a 大疆激光雷达Livox Avia xff0c 电源转接插座 xff0c 内六角形L型扳手 xff0c 镜头清洁布 xff0c 螺钉包 xff0c 说明书 xff0c 1 5米航
  • Go协程与协程池

    1 Golang协程 golang和其它语言最大区别莫过于goroutine xff0c 也就是go的协程 xff0c example如下 xff1a package main import 34 fmt 34 import 34 time
  • Go协程池设计思路(Task-Job-Worker)

    1 铺垫 xff1a Go 的接收器Receiver 在go语言中 xff0c 没有类的概念 xff0c 但是可以给类型 xff08 结构体 xff0c 自定义类型 xff09 定义方法 所谓方法就是定义了接受者的函数 接受者定义在func
  • 系统间通信1:阻塞与非阻塞式通信A

    版权声明 xff1a 本文引用https yinwj blog csdn net article details 48274255 从这篇博文开始 xff0c 我们将进入一个新文章系列 这个文章系列专门整理总结了目前系统间通信的主要原理 手
  • 系统间通信1:阻塞与非阻塞式通信B

    版权声明 xff1a 本文引用https yinwj blog csdn net article details 48274255 接上篇 xff1a 系统间通信1 xff1a 阻塞与非阻塞式通信A 4 3 NIO通信框架 目前流行的NIO
  • 系统间通信2:通信管理与远程方法调用RMI

    本文引用 https yinwj blog csdn net article details 49120813 RMI Remote Method Invocation xff0c 远程方法调用 RPC Remote Procedure C
  • 系统间通信3:RPC的基本概念

    本文引用 https yinwj blog csdn net article details 49453303 1 概述 经过了详细的信息格式 网络IO模型的讲解 xff0c 并且通过JAVA RMI的讲解进行了预热 从这篇文章开始我们将进
  • 系统间通信4:基本IO通信模型

    本文引用 https blog csdn net yinwenjie article details 48472237 目前常用的IO通信模型包括四种 xff1a 阻塞式同步IO 非阻塞式同步IO 多路复用IO和真正的异步IO 所有IO模式