栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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_wait 对 socket 的写事件进行监控。
  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 模式,Proactor 的 epoll_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 来实现。

适用场景:

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/571270.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号