事件处理模式

服务器端主要需要处理三类事件,I/O事件,定时事件和信号
对于这些事件的处理,可以分成两种模式,也就是两种方法。

Reactor—同步IO

主线程(IO处理单元)只负责监听文件描述符上是否有事件发生,有的话立即通知工作线程(逻辑单元),读写数据、接受新连接及处理客户请求均在工作线程中完成。通常由同步I/O实现。
也就是说,主线程就负责喊号,怎么做,你们的事情。
注意:区分同步异步IO的准则就是判断是就绪还是完成,就绪就是同步,完成就是异步。

Proactor—异步IO

主线程和内核负责处理读写数据、接受新连接等I/O操作,工作线程仅负责业务逻辑,如处理客户请求。通常由异步I/O实现。
也就是老板帮忙处理食材,大厨只管炒就行了。

同步I/O模拟Proactor模式

由于异步I/O并不成熟(epoll 是成熟的,但是 epoll 本身是同步的。Linux 上目前没有像 IOCP 这样的成熟异步 IO 实现),实际中使用较少,这里将使用同步I/O模拟实现proactor模式。
同步I/O模型的工作流程如下(epoll_wait为例):

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

并发编程模式

并发编程方法的实现有多线程和多进程两种,但这里涉及的并发模式指I/O处理单元与逻辑单元的协同完成任务的方法。

并发模式中的同步和异步

  • 同步指的是程序完全按照代码序列的顺序执行
  • 异步指的是程序的执行需要由系统事件驱动

    半同步/半异步模式 或 半同步/半反应堆模式

    半同步/半反应堆并发模式是半同步/半异步的变体,将半异步具体化为某种事件处理模式。也就是说,我本来是异步处理一系列事件,反应堆将他细化了。

  • 同步线程用于处理客户逻辑

  • 异步线程用于处理I/O事件
  • 异步线程监听到客户请求后,就将其封装成请求对象并插入请求队列中
  • 请求队列将通知某个工作在同步模式的工作线程来读取并处理该请求对象
  • 半同步/半反应堆工作流程(以Proactor模式为例)

    主线程充当异步线程,负责监听所有socket上的事件若有新请求到来,主线程接收之以得到新的连接socket,然后往epoll内核事件表中注册该socket上的读写事件
    如果连接socket上有读写事件发生,主线程从socket上接收数据,并将数据封装成请求对象插入到请求队列中
    所有工作线程睡眠在请求队列上,当有任务到来时,通过竞争(如互斥锁)获得任务的接管权

也就是线程池负责的是同步的部分,处理逻辑,而epoll实现了一个反应堆。