服务端处理事件的两种模式--Reactor和Proactor

时间:2022-09-09 15:07:38

《Linux高性能服务器编程》阅读笔记:

  服务端程序通常需要处理IO事件信号(参考Linux系统上的信号Linux网络编程的相关信号)、定时器事件(参考定时器实现超时发送/接收和定期检测非活动连接),而Reactor(反射器模式)和Proactor(前摄器模式)正是服务端应对这些事件的两种高效处理模式实现Reactor模式采用同步IO模型,实现Proactor模式则采用异步IO模型。(同步/异步IO参考服务端基本框架和IO模型)。当然还可以使用同步IO方式模拟Prioactor模式。

1. Reactor模式(同步IO实现)

  Reactor模式:主线程(IO处理单元)只负责监听socket文件描述符上是否有事件发生,有的话就立即将该事件通知工作线程(逻辑处理单元)。除此之外,主线程不做任何其他实质性的工作,读写数据、接受新的连接请求、处理客户端请求均在工作线程中完成
  以epoo_wait()为例实现的Reactor模式的工作流程为(所有IO复用技术都是基于同步IO模型):
  (1)主线程往epoll内核事件表中注册socket的可读事件(就绪读),进而调用epoll_wait()监听socket上的可读事件。
  (2)当socket上有数据可读时(含有客户端来连接),epoll_wait()通知主线程,主线程将socket的可读事件放入请求队列。
  (3)唤醒在请求队列上的某个工作线程(请求队列上有多个工作线程,空闲时是休眠状态),工作线程执行相应操作后往该socket上写入请求的处理结果。
服务端处理事件的两种模式--Reactor和Proactor
  从上图可见,Reactor模式中的工作线程并没有区分”读工作线程”和”写工作线程”、”处理工作线程”,所有事件都交由工作根据事件类型来决定如何处理。

2.Proactor模式(异步IO实现)

  Proactor模式是将所有IO事件操作都交由主线程和内核处理,工作线程只负责业务逻辑
  使用异步IO模式实现的Proactor模式的工作流程:(以aio_read()和aio_write()为例)
  (1)主线程调用aio_read()函数向内核注册sockst上的读完成事件,并告诉内核在用户空间的读缓冲区的位置,以及读操作完成时如何通知应用程序(比如信号机制)。
  (2)主线程继续处理业务逻辑。
  (3)当socket上的数据被读入用户缓冲区后(读操作交由内核完成),内核将向应用程序发送信号以通知应用程序数据可用。
  (4)应用程序在预先定义的信号处理函数中选择一个工作线程来处理客户需求。工作线程处理完客户请求后调用aio_write()函数向内核注册socket上的写完成事件,并告诉内核用户写缓冲区的位置以及写操作完成时如何通知应用程序,即写操作交由内核执行。
  (5)主线程继续处理其他业务逻辑。
  (6)当用户缓冲区的数据被内核写入socket后内核向应用程序发送一个信号以通知应用程序发送完毕。
  (7)应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,如决定是否关闭socket连接。
服务端处理事件的两种模式--Reactor和Proactor
  主线程中的epoll_wait()只是用于检测监听socket上的连接而不能用来检测连接socket上的读写事件,因为读写事件是主进程和工作线程通过aio_read()和aio_write()来向内核注册的,内核负责监听读写事件且执行读写操作,读写完毕后再通过信号机制向应用程序报告,应用程序的信号处理函数完成后续操作。

3. 同步IO模拟实现Proactor模式

  Proactor模式的读写socket操作是交由内核处理,即异步IO模式,但是编程使用的IO复用技术是同步IO,我们可以利用同步IO方式模拟出Proactor模式,其原理为:主线程执行数据的读写操作(和监听listen_socket),读写完成后主线程向工作通知这一”完成事件”,那么从工作线程的角度看,和异步IO实现的Proactor模式的工作线程一样,它们直接获得了数据的读写结果,接下来要做的只是对读写结果进行逻辑处理
  以epoll_wait()为例,使用同步IO模型模拟Proactor模式的工作流程:
  (1)主线程往epoll内核事件表中注册socket上的读就绪事件,并调用epoll_wait()等待socket上有数据可读(有客户端连接也是可读)
  (2)当socket上有数据可读时epoll_wait()将通知主线程,主线程从socket循环读取数据直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。主线程调用epoll_wait()等待socket可写,以返回数据给该socket。
  (3)请求队列上某个睡眠的工作线程被唤醒,它获得请求对象并处理该请求,然后往epoll内核事件表中注册socket上的写就绪事件(目的是要返回数据给该socket)。
  (4)当socket可写时,epoll_wait()通知主线程,主线程往socket上写入服务端处理客户端的请求结果,即工作线程的数据。
服务端处理事件的两种模式--Reactor和Proactor