Linux服务器 | 事件处理模式:Reactor模式、Proactor模式

文章目录

  • Reactor模式
  • Proactor模式
    • 同步I/O模型模拟Proactor模式
  • 两者的优缺点
    • Reactor
    • Proactor


同步I/O模型通常用于实现 Reactor 模式,异步I/O模型通常用于实现 Proactor 模式。(不是绝对的,同步I/O也可模拟出 Proactor 模式)

Reactor模式

原理

Reactor 模式要求主线程(I/O处理单元)只负责监听文件描述符上是否有事件就绪,如果有则将该就绪事件通知给工作线程(逻辑单元)。除此之外主线程不会进行其他实质性的工作,读写数据、接收新连接、业务逻辑处理全部在工作线程中完成。

工作流程

这里以 epoll_wait 为例,使用同步I/O模型实现的 Reactor 模式的工作流程如下:

  1. 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件。
  2. 主线程调用 epoll_wait 开始对 socket 的读事件进行监控。
  3. 如果 socket 读就绪,epoll_wait 会通知主线程,主线程则将 socket 可读事件(即 socket 连接本身) 放入请求队列中。
  4. 请求队列上某个休眠的工作线程被唤醒,此时会从 socket 中读取数据,并且处理用户请求,然后往 epoll 内核事件表中注册该 socket 的写就绪事件。
  5. 主线程继续调用 epoll_waitsocket 的写事件进行监控。
  6. socket 写就绪时,epoll_wait 会通知主线程,主线程则将 socket 可写事件(即 socket 连接本身) 放入请求队列中。
  7. 请求队列上某个休眠的工作线程被唤醒,将服务器处理客户请求的结果写入到 socket

在这里插入图片描述
工作线程从请求队列中取出事件后,根据事件类型来决定如何处理事件,所以不会区分 读工作线程写工作线程


Proactor模式

原理

Proactor模式则是将所有的I/O操作全部交给主线程内核处理工作线程仅仅负责业务逻辑。

工作流程

这里以 aio 为例,使用异步I/O模型实现的 Reactor 模式的工作流程如下:

  1. 主线程调用 aio_read 向内核注册 socket 上的读完成事件,并且告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序(这里以信号为例)。
  2. I/O事件交给内核进行异步处理,此时主线程继续处理其他逻辑(区别于 Reactor 中主线程需要持续监控就绪事件)。
  3. socket 上的数据已被读入用户缓冲区后,内核向应用程序发送一个信号,通知其数据已可用。
  4. 通过应用程序预先定义好的信号处理函数来选择一个工作线程以处理客户请求。
  5. 工作线程处理完客户请求之后会调用 aio_write 向内核注册 socket 上的写就绪事件,并且告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序(仍选择使用信号)。
  6. 主线程继续处理其他逻辑(同2)。
  7. 当用户缓冲区的数据被写入 socket 后,内核向应用程序发送一个信号,通知其数据发送完成。
  8. 通过应用程序 注册(预先定义好) 的信号处理 事件(函数) 来选择一个工作线程来进行善后处理,例如是否关闭 socket

在这里插入图片描述

由于读/写事件是通过 aio_read/aio_write 向内核中进行 注册 的,并由内核通过 信号 向应用程序 报告 的。因此,不同于 Reactor 模式,Proactorepoll_wait 仅仅用来监听 socket 上是否有新的连接请求到来,而不用于 注册 or 报告 读/写事件。


同步I/O模型模拟Proactor模式

原理

主线程执行数据读写操作,完成后向工作线程通知事件的完成。从工作线程的角度来看,他们就直接获得了数据读写的结果,接下来的工作就只需要对读写结果进行业务逻辑处理。

工作流程

这里以 epoll_wait 为例,使用同步I/O模型实现的 Proactor 模式的工作流程:

  1. 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件。(同步I/O注册就绪事件、异步I/O注册完成事件)
  2. 主线程调用 epoll_wait 等待 socket 上有数据可读。
  3. socket 上有数据可读,epoll_wait 通知主线程,主线程从 socket 中循环读取数据,直到没有数据可读。然后将读到的数据封装成一个请求对象插入请求队列中。
  4. 请求队列上某个休眠的工作线程被唤醒,此时它会获取请求对象并且处理客户请求,然后往 epoll 内核事件表中注册 socket 的写就绪事件。
  5. 主线程调用 epoll_wait 等待 socket 可写。
  6. 如果 socket 写就绪,epoll_wait 通知主线程,主线程往 socket 中写入服务器处理客户请求的结果。

在这里插入图片描述


两者的优缺点

Reactor

Reactor 实现了一个被动的事件分离和分发模型:

  • 主线程只负责监听读写事件是否就绪,就绪后放入请求队列,并唤醒请求队列上某个工作线程;
  • 工作线程读写数据处理客户请求

优点:

  • 实现相对简单,对于耗时短的处理场景处理高效。
  • 操作系统可以在多个事件源上等待,并且避免了多线程编程相关的性能开销和编程复杂性。
  • 事件的串行化对应用是透明的,可以顺序的同步执行而不需要加锁。
  • 将与应用无关的多路分解、分配机制和与应用相关的回调函数分离开来。

缺点:

  • 处理耗时长的操作会造成事件分发的阻塞,影响到后续事件的处理。

适用场景:

同时接收多个服务请求,并且依次同步处理它们的事件驱动程序。


Proactor

Proactor 实现了一个主动的事件分离和分发模型:

  • 主线程监听事件是否就绪;
  • 内核执行I/O操作读写数据;
  • 上一步完成后根据预先注册好的信号函数选择一个工作线程处理客户请求。

优点:

  • 性能更高,能够适应耗时长的并发场景(各个任务间互不影响);
  • 这种设计允许多个任务并发的执行,从而提高吞吐量。

缺点:

  • 实现逻辑复杂,依赖操作系统对异步的支持,目前实现了纯异步操作的操作系统少。
    1. 实现优秀的如 windows IOCP,但由于 windows 系统用于服务器的局限性,目前应用范围较小。
    2. Unix/Linux 系统对纯异步的支持有限,应用事件驱动的主流方案还是通过 select/epoll 来实现。

适用场景:

异步接收同时处理多个服务请求的事件驱动程序。

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

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

相关文章

Linux服务器 | 服务器模型与三个模块、两种并发模式:半同步/半异步、领导者/追随者

文章目录两种服务器模型及三个模块C/S模型P2P模型I/O处理单元、逻辑单元、存储单元并发同步与异步半同步/半异步模式变体:半同步/半反应堆模式改进:高效的半同步/半异步模式领导者/追随者模式组件 :句柄集、线程集、事件处理器工作流程两种服…

字符串匹配之KMP(KnuthMorrisPratt)算法(图解)

文章目录最长相等前后缀next数组概念代码实现图解GetNext中的回溯改进代码实现代码复杂度分析最长相等前后缀 给出一个字符串 ababa 前缀集合:{a, ab, aba, abab} 后缀集合:{a, ba, aba, baba} 相等前后缀 即上面用同样颜色标识出来的集合元素&#…

Android入门(一) | Android Studio的配置与使用

文章目录安装配置Android Studio使用Android Studio模拟器更改Android SDK的路径Hello World!安装配置Android Studio 从这一步开始: 一直点 next 即可,直到存储路径的选择上,可以放到非 C 盘,这里我放到 D 盘了&am…

Android 入门(四) | Intent 实现 Activity 切换

文章目录Intent显式 Intent定义两个 xml 文件android:orientationmatch_parent 和 wrap_contentIntent函数定义两个 Activity隐式 Intent更多隐式 Intent 的用法用隐式 Intent 打开系统浏览器自建 Activity 以响应打开网页的 Intent向下一个活动传递数据返回数据给上一个活动In…

Android入门(二) | 项目目录及主要文件作用分析

文章目录项目目录分析app目录分析AndroidManifest.xml 分析MainActivity.kt 分析build.gradle 分析最外层目录下的 build.gradleapp 目录下的 build.gradle项目目录分析 我们来看一下 src/main/res 下的一些文件: .gradle 和 .idea :这两个目录下放置…

Android入门(三) | Android 的日志工具 Logcat

文章目录日志工具类 android.util.LogLogcat 中的过滤器日志工具类 android.util.Log Log 从属日志工具类 android.util.Log ,该类提供了五个方法供我们打印日志: Log.v() :用于打印那些最为琐碎的、意义最小的日志信息。对应级别 verbose&…

Android入门(五) | Activity 的生命周期

文章目录Activity 的状态及生命周期实现管理生命周期FirstActivitySecondActivityDialogActivity运行结果旧活动被回收了还能返回吗?Activity 的状态及生命周期 Android 的应用程序运用 栈(Back Stack) 的思想来管理 Activity: …

Android入门(六) | Activity 的启动模式 及 生产环境中关于 Activity 的小技巧

文章目录Activity 的启动模式standardsingleTopsingleTasksingleInstance技巧了解当前界面是哪个 Activity随时随地退出程序启动活动的最佳写法Activity 的启动模式 standard:默认的启动方式,每次启动一个活动都会重新创建singleTop:如果该活…

Android入门(七) | 常用控件

文章目录TextView 控件:文本信息Button 控件:按钮EditText 控件:输入框ImageView 控件:图片ProgressBar 控件:进度条AlertDialog 控件:提示框ProgressDialog 控件:带有进度条的提示框TextView 控…

Android入门(八) | 常用的界面布局 及 自定义控件

文章目录LinearLayout :线性布局android:layout_gravity :控件的对齐方式android:layout_weight:权重RelativeLayout :相对布局相对于父布局进行定位相对于控件进行定位边缘对齐FrameLayout :帧布局Percent &#xff1…

Android入门(九)| 滚动控件 ListView 与 RecyclerView

文章目录ListView内置类型的简单运用定制数据类型提升效率点击事件RecyclerView布局管理器点击事件ListView 内置类型的简单运用 由于手机屏幕空间有限,能够一次性在屏幕上显示的内容不多,当我们的程序有大量数据需要显示的时候就可以借助 ListView 来…

Android入门(10)| Fragment碎片详解

文章目录为什么要使用碎片(Fragment)实例布局文件FragmentActivity动态添加碎片布局文件FragmentActivity碎片通信Fragment布局文件Activity生命周期为什么要使用碎片(Fragment) 我们在手机上看新闻可能是这样的: Re…

Android开发(1) | Fragment 的应用——新闻应用

文章目录Item&#xff1a;标题子项布局文件Java代码标题碎片布局文件Java代码新闻内容碎片布局文件Java代码新闻内容活动布局文件Java代码首界面布局文件Java代码Item&#xff1a;标题子项 布局文件 news_item.xml&#xff1a; <TextViewxmlns:android"http://schema…

Android入门(11)| 全局广播与本地广播

文章目录广播概念接收广播动态注册实例静态注册实例发送广播发送标准广播广播的跨进程特性发送有序广播本地广播广播概念 Android 中的每个应用程序都可以对自己感兴趣的广播进行注册&#xff0c;这样该程序就只会接收到自己所关心的广播内容&#xff0c;这些广播可能是来自系…

Android开发(2) | 广播 Broadcast 的应用——强制下线功能

文章目录功能简介关闭所有活动登陆界面发送强制下线的广播广播接收器AndroidManifest.xml运行结果功能简介 强制下线功能只需要弹出一个对话框&#xff0c;让用户只能点击确定按钮&#xff0c;回到登录界面。 如果在每一个活动中添加一个对话框的话太过繁琐&#xff0c;用广播…

Android入门(12)| 数据持久化

文章目录数据持久化文件存储将数据存储进文件实例从文件中读取数据实例SharedPreferences存储将数据存储进文件实例从文件中读取数据实例实现记住密码的功能SQLite数据库存储创建自己的帮助类调用自己的帮助类补全 onUpgrade() 方法增删查改增&#xff1a;SQLiteDatabase.inser…

Android入门(13)| Android权限 与 内容提供器

文章目录普通权限与危险权限运行时申请权限内容提供器运用安卓封装好的内容提供器自实现的内容提供器概念实现普通权限与危险权限 主要用于不同应用程序之间在保证被访数据的安全性的基础上&#xff0c;实现数据共享的功能。 在 Android 6.0 开始引入了运行时权限的功能&…

Android入门(14)| 通知

文章目录创建通知点击效果其它小功能实例创建通知 创建通知的步骤&#xff1a; 管理通知的 NotificationManager&#xff0c;通常通过当前 Context 的 getSystemService() 获取实例。它接受一个字符串参数用于确定获取系统的什么服务。Android 8.0(O) 版本后需要通知通道&…

Android开发(3) | 权限和内容提供器的应用——调用相机和相册

文章目录拍照并保存到 ImageView 控件布局文件 notice_layout.xml按钮 button_takePhoto 的点击操作隐式 Intent 启动后的回调AndroidManifest.xml从相册选取照片并在 ImageView 控件中显示布局文件 notice_layout.xml按钮 button_takePhoto 的点击操作自定义打开相册的方法 op…

Android开发(4) | 系统权限、MediaPlayer类 和 VideoView类 的应用——播放多媒体文件

文章目录MediaPlayer类播放音频的实例VideoView类播放视频的实例MediaPlayer类 对多种格式的音频文件提供了全面的控制方法&#xff1a; 如何获得MediaPlayer实例&#xff1f; 通过构造函数&#xff1a; MediaPlayer mp new MediaPlayer();调用 MediaPlayer.create() 方法&…