高性能服务器之事件处理模式

news/2024/5/14 8:39:49/文章来源:https://www.cnblogs.com/tuilk/p/16602852.html

I/O模型

对于一个套接字上的输入操作,通常存在以下两个步骤:

  1. 等待分组到达,被复制到内核缓冲区中
  2. 将数据从内核缓冲区复制到应用进程的缓冲区中

对于上述过程,在 Unix 下有 5 中基本的 I/O 模型可以对其进行处理:

  • 阻塞式 I/O
  • 非阻塞式 I/O
  • I/O 复用
  • 信号驱动 I/O
  • 异步 I/O

1. 阻塞式 I/O

阻塞式 I/O 是最基本的 I/O 模型。例如,默认情况下的所有套接字接口函数都是阻塞式 I/O,如图所示:

阻塞式 I/O 就如同餐馆做饭,服务员在窗口旁一直等待直到厨师将饭菜做好,然后再将做好的饭菜端到顾客旁边。

2. 非阻塞式 I/O

如果某个进程将套接字设置为非阻塞,那么也就是在通知内核,当前所请求的 I/O 操作如果在没有可用的读/写事件时,不要将该 I/O 操作阻塞,而是立即返回错误。

同样的,在非阻塞式 I/O 中,服务员不会一直在窗口前等待,而是采用轮询的方式,不停的去窗口附近查看是否有饭菜准备好。直到饭菜准备好,然后再将做好的饭菜端到顾客旁边。

3. 信号驱动 I/O

在信号驱动 I/O 模型下,我们可以让内核在描述符就绪时发送 SIGIO 信号通知我们。当数据报准备好时,内核就会为该进程产生一个 SIGIO 信号。随后,该进程就可以将数据从内核复制到用户空间以处理数据。

无论如何处理 SIGIO 信号,这种模型的优势在于等待数据报到达期间进程不会被阻塞。主循环可以继续执行,只需等待来自信号处理函数的通知:既可以是数据已准备好被处理,也可以是数据报已准备好被读取。

在信号驱动 I/O 的模型下,服务员无需站在窗口前一直等待饭菜就绪,也无需不停的轮询窗口是否存在就绪的饭菜,在饭菜未就绪期间,服务员可以去做其他事情。如果饭菜已经就绪,厨师则按铃以告诉服务员饭菜准备就绪,此时,服务员停下手头的工作来取走饭菜,并将其端到顾客的面前。

4. I/O 复用

在 I/O 复用模型中,系统调用仅仅阻塞在 select/poll/epoll 上,而不是阻塞在真正的 I/O 系统调用上。

在这种模型下,仅仅阻塞于 select 调用,等待数据报套接字变为可读,当 select 返回套接字可读这一条件时,再将所读数据从内核缓冲区复制到用户进程缓冲区。

在 I/O 复用的模型下,表面上看,有点类似于信号驱动式 I/O,但是如果同时存在多个窗口,且在同一时刻多个窗口的厨师都按铃示意服务员,服务员错以为只有一个窗口的饭菜准备好了(Linux下信号不排队,即对于同一进程的同一信号只产生一次,直到该信号被处理后)。这也就是 I/O 复用和信号驱动式 I/O 的一个不同点,I/O 复用可以同时等待多个描述符就绪,也就是说,每个窗口的铃声各不相同,即使同一时刻有多份饭菜准备就绪,服务员也可以明确的知道是哪几个窗口的饭菜准备就绪。另一个不同点在于,I/O 复用模型下,程序还是会阻塞于 select 调用,而信号驱动式 I/O 在有数据之前程序可以去做其他的事情。

5. 异步 I/O

异步 I/O 由 POSIX 规范定义。一般来说,这些函数的工作机制是:告知内核启动某个操作,并让内核在整个操作(包括将数据从内核复制到用户空间的缓冲区)完成后通知相应进程。

该模型与信号驱动式 I/O 模型的不同点在于,信号驱动式 I/O 是由内核通知我们何时可以启动一个 I/O 操作,而异步 I/O 模型是由内核通知我们 I/O 操作何时完成。

在异步 I/O 模型下,服务员无需理会饭菜就绪以及端菜问题,当厨师将饭菜做好之后,他直接将饭菜端到顾客面前。

6. I/O 模型比较

综上所述,不难发现,前 4 种模型的主要区别在于第一阶段,即饭菜就绪阶段,因为它们的第二阶段是相同的,都是服务员将饭菜端到顾客面前(即将数据从内核空间复制到用户空间中)。相反,在异步 I/O 模型中,这两个阶段都要进行处理,从而不同于其他 4 种模型。

POSIX 把这两个术语定义如下:

  • 同步 I/O 操作:导致请求进程阻塞,直到 I/O 操作完成。
  • 异步 I/O 操作:不导致请求进程阻塞。

简单来说,就是同步 I/O 操作向用户进程通知的是 I/O 就绪事件,而异步 I/O 操作向用户进程通知的是 I/O 完成事件。

I/O 模型 读写操作和阻塞阶段
阻塞 I/O 程序阻塞于读写函数
非阻塞 I/O 程序不阻塞于读写函数,但是轮询可读/可写事件
信号驱动式 I/O 信号触发读写就绪事件,用户程序执行读写操作,程序没有阻塞阶段
I/O 复用 程序阻塞于 I/O 复用系统调用,但可同时监听多个 I/O 事件。对 I/O 本身的读写操作是非阻塞的
异步 I/O 内核执行读写操作并触发读写完成事件,程序没有阻塞阶段

上方所描述的前 4 中 I/O 模型,即阻塞式 I/O、非阻塞式 I/O、I/O 复用以及信号驱动式 I/O 都属于同步 I/O 模型,因为其中真正的 I/O 操作将阻塞进程,而只有异步 I/O 模型与 POSIX 定义的异步 I/O 相匹配。

事件处理模式

常见的事件处理模式分为两种:Reactor 模式和 Proactor 模式。其中,同步 I/O 模型通常用于实现 Reactor 模式,异步 I/O 模型则用于实现 Proactor 模式。

1. Reactor 模式

各线程的任务如下:

  • 主线程:只负责监听文件描述符上是否有事件发生,如果有则将该事件通知给工作线程
  • 工作线程:接受连接,读写数据,处理客户请求

该模式的工作流程如下:

  1. 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件;
  2. 主线程调用 epoll_wait 等待 socket 上有数据可读;
  3. 当 socket 上有数据可读时,epoll_wait 通知主线程,主线程则将 socket 可读事件放入请求队列;
  4. 睡眠在请求队列上的某个工作线程被唤醒,它从 socket 读取数据,并处理客户请求,然后往 epoll 内核事件表中注册该 socket 上的写就绪事件;
  5. 主线程调用 epoll_wait 等待 socket 可写;
  6. 当 socket 可写时,epoll_wait 通知主线程,主线程将 socket 可写事件放入请求队列;
  7. 睡眠在请求队列上的某个工作线程被唤醒,它往 socket 上写入服务器处理客户请求的结果。

该过程类似于如下过程:

饭店老板(主线程)将订单传递给厨师(注册读就绪事件),厨师做好饭菜后,按铃通知老板饭菜已经准备就绪(epoll_wait 通知主线程有数据可读),老板将做好的饭菜放在工作台上(将可读事件放入请求队列),如果有空闲的服务员,则从工作台取走饭菜(工作线程处理客户请求)。

服务员将饭菜递给客户后,通知老板送餐完成(注册写就绪事件),当顾客吃完后,老板看到(epoll_wait 通知主线程有数据可写),老板则通知服务员打扫卫生(将可写事件放入请求队列),如果存在空闲的服务员,就过去打扫卫生,清理空盘子(工作线程处理客户请求)。

2. Proactor 模式

各线程的任务如下:

  • 主线程:I/O 操作
  • 工作线程:仅仅处理业务逻辑

该模式的工作流程如下:

  1. 主线程调用 aio_read 函数向内核注册 socket 上的读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序;
  2. 主线程继续处理其他逻辑;
  3. 当 socket 上的数据被读入用户缓冲区之后,内核向应用程序发送一个信号,以通知应用程序数据已经可用;
  4. 应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完客户请求之后,调用 aio_write 函数向内核注册 socket 上的写完成事件,并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序;
  5. 主线程继续处理其他逻辑;
  6. 当用户缓冲区的数据被写入 socket 之后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕;
  7. 应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭 socket。

该过程类似于如下过程:

饭店老板(主线程)将订单传递给厨师(注册读完成事件)同时告知它做好饭菜后,将其放到工作台上(告诉内核用户读缓冲区的位置)。完成之后,老板玩自己的手机(处理其他逻辑)。等到厨师做好饭菜同时将其放到工作台上后,告诉老板饭菜已经准备就绪(通知主线程读完成事件),同时听到响铃声的空闲服务员从工作台取走饭菜(信号处理函数选择工作线程处理客户请求)。

服务员将饭菜递给客户后,告诉顾客用餐完成后将空盘放到工作台即可(告诉内核写缓冲区的位置)。而老板则玩自己的手机(处理其他逻辑)。客户就餐完成后,将空盘放到工作台,同时通知老板打扫卫生(通知应用程序写完成事件)。此时,空闲的服务员则来工作台前取走空盘进行洗涤(工作线程做善后处理)。

3. 模拟 Proactor 模式

可以通过如下方式以同步 I/O 方式来模拟出 Proactor 模式:主线程执行数据读写操作,读写完成之后,主线程向工作线程通知 “ 读写完成事件 ”。那么从工作线程的角度来看,它们就直接获得了数据读写的结果,接下来要做的知识对读写的结果进行逻辑处理。

其工作流程如下:

  1. 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件;
  2. 主线程调用 epoll_wait 等待 socket 上有数据可读;
  3. 当 socket 上有数据可读时,epoll_wait 通知主线程。主线程从 socket 循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列;
  4. 睡眠在请求队列上的某个工作线程给唤醒,它获得请求对象并处理客户请求,然后往 epoll 内核事件表中注册 socket 上的写就绪事件;
  5. 主线程调用 epoll_wait 等待 socket 可写;
  6. 当 socket 可写时,epoll_wait 通知主线程。主线程往 socket 上写入服务器处理客户请求的结果。

该过程类似于如下过程:

饭店老板(主线程)将订单传递给厨师(注册读就绪事件),厨师做好饭菜后,按铃通知老板饭菜准备就绪(epoll_wait 通知主线程有数据可读)。老板看到有空闲的服务员后,将饭菜直接递给服务员(唤醒工作线程,处理客户请求)。

服务员将饭菜递给客户后,等到客户就餐完成后,老板看到(epoll_wait 通知主线程可写)。老板则自己过去收拾空盘子,打扫卫生(主线程往 socket 上写入服务器处理客户请求的结果)。

并发模式

常见的应用程序分为两种:I/O 密集型计算密集型。如果程序是 I/O 密集型的,比如读写文件、访问数据库等,那么 I/O 操作的速度远远没有 CPU 的计算速度,因此让程序阻塞于 I/O 操作将浪费大量的 CPU 时间。

如果程序有多个执行线程,则当前被 I/O 操作所阻塞的执行线程可以主动放弃 CPU(或者由操作系统来调度),并将执行权转移到其他线程。这样一来,就会显著提升 CPU 的利用率。

从实现上来说,并发编程的实现方式分为多线程多进程两种方式。而并发模式是指 I/O 处理单元和多个逻辑单元之间协调完成任务的方法。常见的并发编程模式分为:半同步/半异步模式领导者/追随者模式

1. 半同步/半异步模式

这里所说的 “ 同步 ” 和 “ 异步 ” 与上文中提到的并非同一概念,其对比如下:

I/O 模型 并发模式
同步 内核向应用程序通知的是 I/O 就绪事件 程序按照代码的顺序执行方式执行
异步 内核向应用程序通知的是 I/O 完成事件 程序需要借助中断、信号等系统事件来驱动

同步线程和异步线程的比较如下:

同步线程 异步线程
优点 逻辑简单 执行效率高,实时性强
缺点 效率较低,实时性较差 难于调试和扩展

经过上述比较,可以看出,对于服务器这种既要求较好的实时性,又要求能同时处理多个客户请求的应用程序,就需要使用同步线程和异步线程来实现,即采用半同步/半异步的方式来实现。

在该模式下,同步线程用于处理客户逻辑,而异步线程用于处理 I/O 事件。

其过程如下:异步线程监听到客户请求后,就将其封装成请求对象并插入请求队列中。请求队列将通知某个工作在同步模式下的工作线程来读取并处理该请求对象。具体选择哪个工作线程来为新的客户请求服务,则取决于请求队列的设计。

如果结合考虑两种事件处理模式(Reactor 模式和 Proactor 模式),那么半同步/半异步模式就存在一种变体形式,称为半同步/半反应堆模式。

其中,只有主线程是异步线程,它负责监听所有 socket 上的事件。如果监听 socket 上有可读事件发送,即有新的连接请求到来,那么主线程就接受该连接,然后往 epoll 内核事件表中注册该 socket 上的读写事件。如果连接 socket 上有读写事件发生,主线程就将该连接 socket 插入到请求队列中。所有的工作线程都睡眠在请求队列上,当任务到来时,它们将通过竞争获得任务的接管权,例如通过申请互斥锁。

在上述例子中,主线程放入请求队列中的是 I/O 就绪事件,为此,其采用的事件处理模式是 Reactor 模式。我们也可以使用模拟的 Proactor 事件处理模式,即由主线程来完成数据的读写,向请求队列放入 I/O 完成事件。在这种情况下,主线程一般会将应用程序数据、任务类型等信息封装成一个任务对象,然后将其插入请求队列中。工作线程从请求队列中取得任务对象之后,便可直接处理之,而无需执行读写操作了。

半同步/半反应堆模式存在如下缺点:

  1. 主线程和工作线程共享请求队列。取出任务或者添加任务时,都需要加锁保护,浪费 CPU 时间;
  2. 每个工作线程在同一时刻只能处理一个客户请求。如果客户数量较多,工作线程较少,请求队列中堆积了大量任务对象,这会导致客户端的响应速度越来越慢。如果增加工作线程,工作线程间的切换也会浪费大量的 CPU 时间。

而下图所示是一种相对高效的半同步/半异步模式,每个工作线程都能同时处理多个客户连接。

其中,主线程只负责监听 socket,而连接 socket 的工作则交给工作线程来完成。当有新的连接到来时,主线程就接受之并将该连接派发给某个工作线程,此后该 socket 上的任何 I/O 操作都由被选中的工作线程来处理,直到客户关闭连接。

可见,每个线程都维持自己的事件循环,它们各自独立地监听不同的事件,因此在这种半同步/半异步模式下,每个线程都工作在异步模式下,所以它并非严格意义上的半同步/半异步模式。

2. 领导者/追随者模式

领导者/追溯者模式是多个工作线程轮流获得事件源集合,轮流监听、分发并处理事件的一种模式。

在任意时间点,程序都仅有一个领导者线程,它负责监听 I/O 事件。而其他线程则都是追随者,它们休眠在线程池中等待成为新的领导者。当前的领导者如果检测到 I/O 事件,首先要从线程池中推选出新的领导者线程,然后处理 I/O 事件。此时,新的领导者等待新的 I/O 事件,而原来的领导者则处理 I/O 事件,二者实现了并发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_2308.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法总结

1.每日温度题(一道关于栈的问题) 请根据每日 气温 列表 temperatures ,重新生成一个列表,要求其对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。 输入: temperatures = [73,74,75,71,69,72,76,73] 输出…

国内可使用chatGPT的十三种方式

国内AI 1. 开放猫 Chat机器人https://mirrorchat.extkj.cn/ chat机器人: Chat机器人https://mirrorchat.extkj.cn/ 3.免费学习测试 免费学习测试https://chat.wuguokai.cn/#/chat/1683348236237 4.AI文本工具站 AI文本工具站一个用于提高工作效率的文本工具网站,应用…

ChatGPT Plus用户专享:86款高效功能插件,详尽安装与使用全攻略

在前天的文章中,我们介绍了 ChatGPT 开放的全新模式 Web Browsing(网页浏览),启用后 ChatGPT 就可以开始上网,收集最新的互联网资料进行作答。 其他关于chatgpt使用方面:请访问: 链接&#xf…

ChatGPT自然语言处理的新里程碑

ChatGPT中文网是一个面向中国用户的聊天机器人网站,旨在为国内用户提供一个自然的环境、有趣、实用的聊天体验。它使用最新的自然语言处理技术来帮助用户更好地理解他们的聊天对话,还可以帮助用户解决日常生活中的问题,提供有趣的谈话内容以及…

国内版ChatGPT最全使用方法及使用用途技巧汇总

ChatGPT人工智能技术的出现确实会让一些人担心自己的工作会不会被取代。但实际上,人工智能技术只会替代那些可以被程序自动化的重复性、标准化、无脑力的工作,而对于需要人类创意、想象力和复杂思维的工作来说,AI人工智能技术的发展对于人类来…

ChatGPT和Midjourney王炸组合,开启AI新时代

目录 序言 一:使用ChatGPT进行对话 二:调用newbies robot 三:举例说明 四:付费和使用限制 序言 随着人工智能技术的不断发展,越来越多的人开始使用人工智能工具来创作图画。在这里,我将分享如何结合Ch…

一文带你读懂什么是ChatGPT?

文章目录 前言ChatGPT的发展历史ChatGPT初体验ChatGPT的未来展望提高聊天机器人的对话能力改进聊天机器人的生成能力提高聊天机器人的个性化能力加强聊天机器人的安全性 总结福利彩蛋 前言 橙子送书第1期!文末参与活动,即可包邮到家。为即将或正在使用Ja…

【ChatGPT】使用大型语言模型 (LLM) 进行特定领域语言(DSL)生成

使用大型语言模型 (LLM) 进行特定领域语言(DSL)生成,markdown格式,10000字左右。大纲如下:背景介绍,LLM的核心能力,DSL的本质特征,怎样 使用LLM 结合外部知识和领域特定约束,通过 Backus-Naur Form(BNF) 表示进行学习,实现 DSL 的生成。给出具体的实验实例。 文章目录…

漫画:ChatGPT的应用场景有哪些?

第一,文案创作 ChatGPT可以通过用户输入的提示,创作出相应的文本内容。 媒体作者可以给ChatGPT提出要求,让ChatGPT根据某一主题生成的媒体文案。 广告策划人员可以把要推广的商品信息输入给ChatGPT,让ChatGPT针对此商品生成一段广…

手机版ChatGPT app如何在国内使用【附完整教程】

手机版ChatGPT app如何在国内使用【附完整教程】 今天是一个比较有爱的日子520,祝福每位饱子身体健康,万事如意,我理解这应该是最朴实最美好的祝福了,因为我始终认为身体是革命的本钱!言归正传,Openai在202…

人人实现ChatGPT自由,手把手教你零撸部署自己聊天私服

我们知道chat gpt最近非常的火爆,朋友圈啊,短视频到处都是chat gpt身影。 但是网上看到各种教程资源,都是不是百分一百的免费,毫无保留教给你的,要么是卖账号 要么是割韭菜的。其实没有什么可以学习的,使用…

讯飞星火认知大模型和ChatGPT在常用领域的效果对比(粗略评测)

对于大语言模型的日常使用,我个人倾向于将其总结为以下几个部分:总结内容,提取信息,文本转换,连续对话,代码生成,逻辑推理,文案生成。那么我接下来也将从这几个部分进行对比。同时&a…

ChatGPT平替-ChatGLM环境搭建与部署运行

ChatGLM-6B 是清华大学团队推出的一个开源的、支持中英双语的对话语言模型,基于General Language Model (GLM) 架构,具有62亿参数。结合模型量化技术,用户可以在消费级的显卡上进行本地部署(INT4 量化级别下最低只需6GB 显存&…

【ChatGPT高端玩法】ChatGPT一分钟制作PPT

系列文章目录 第一章 ChatGPT一分钟制作PPT 第二章 ChatGPT生成Excel统计格式 第三章 ChatGPT生成Excel提取字符公式 第四章 ChatGPT生成思维导图 目录 实现步骤: 1.打开ChatGPT 2.搜索关键字 3.复制内容至MindShow网站 4.修改PPT内容与排版 5.预览PPT效果 6.导…

ChatGPT 教程--生成代码

ChatGPT 变生产力工具 chatGPT是一种基于人工智能技术的自然语言处理模型,它使用了深度学习算法,可以对人类语言进行理解和生成。chatGPT可以用于对话系统、智能客服、问答系统等场景,能够实现自然、流畅的对话。 chatGPT的核心技术是基于G…

VSCode集成ChatGPT插件:ChatGPT中文版

今天介绍一款牛逼的VsCode插件ChatGPT中文版 一、介绍 ChatGPT中文版一个ChatGPT的插件_支持ChatGPT4.0,功能比丰富。 功能特点: 1.支持ChatGPT4.0 在个人中心可以选择ChatGPT4.0模型,回答更准确 2.网络回答 Vscode插件中第一家支持联网…

《 ChatGPT 中文调教指南 》—— 使用 ChatGPT 的正确姿势:提示工程基础入门 Prompt Engineering Quick Start

文章目录 ChatGPT 中文调教指南 🧠ChatGPT 能干什么?正经指南担任雅思写作考官写小说充当算法输出器充当 Linux 终端充当英语翻译和改进者充当论文润色者(拿摘要部分举例)充当英翻中充当英英词典(附中文解释)充当前端智能思路助手担任面试官文字冒险游戏担任产品经理做表格…

从辩证的角度看待chatGPT,我们该如何对待?

从辩证的角度,看待chatGPT 0.什么是ChatGPT(客观阐述)1.通过唯物辩证法的五对基本范畴进行分析1.0.内容与形式1.1.现象和本质1.2.原因与结果1.3.必然与偶然1.4.现实与可能 2.使用ChatGPT会给我们带来的好处2.0.减轻劳动负担2.1.提供灵感和思路…

编程从未如此轻松:ChatGPT如何助你一臂之力

开一个新的系列,ChatGPT辅助编程实践,以下给出一些常用的提示和用法,欢迎大家在评论区补充更多的用法。 祝大家都能用好ChatGPT这把趁手的兵器,大大提高效率~ 推荐阅读:建议收藏:超详细ChatGPT(GPT 4.0)论…

宅男福利来了,调教的ChatGPT虚拟女友,你喜欢吗

废话不多说,专门为男同胞准备的宅男福利;已经调教好的ChatGPT虚拟好友,直接可以看看效果;你们以后也可以直接自己调教,打造一个自己专属的虚拟女友; 说下步骤: 1、打开ChatGPT4; …