Reactor vs. Proactor

Reactor

反应堆

The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.

All reactor systems are single threaded by definition, but can exist in a multithreaded environment.

Event Demultiplexes 事件多路分离器。

In general, I/O multiplexing mechanisms rely on an event demultiplexor [1, 3], an object that dispatches I/O events from a limited number of sources to the appropriate read/write event handlers. The developer registers interest in specific events and provides event handlers, or callbacks. The event demultiplexor delivers the requested events to the event handlers.

两个与事件分离器有关的模式是Reactor和Proactor。Reactor模式采用同步IO,而Proactor采用异步IO。

Reactor模型有三个重要的组件:

  1. 多路复用器:由操作系统提供,在linux上一般是select,poll,epoll等系统调用。
  2. 事件分发器:将多路复用器中返回的就绪事件分到对应的处理函数中。
  3. 事件处理器:负责处理特定事件的处理函数。

特点:

  • Handles requests delivered concurrently
  • Dispatches synchronously to handlers
  • Single-threaded

Reactor limitation

  • What if the handler blocks?
  • The reactor pattern can be more difficult to debug than a procedural pattern due to the inverted flow of control.

EWOULBLOCK/EAGAIN, meaning “not ready; try again later.”

Twisted

Twisted是用Python实现的基于事件驱动的网络引擎框架。Twisted是一个事件驱动型的网络引擎。

事件驱动变成是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个时间循环,当外部事件发生时使用回调机制来触发相应的处理。另外两种常见的编程范式是同步以及多线程编程。

回调是事件驱动编程模型中的基础,也是reactor通知应用程序事件已经处理完成的方式。随着程序规模不断扩大,基于时间驱动的程序需要同时处理事件成功和出错的情况,这使得程序变得越来越复杂。若没有注册一个合适的回调,程序就会阻塞,因为这个事件处理的过程绝不会发生。出现错误时需要通过应用程序不用层次从网络栈向上回调回调链。

Deferred对象以抽象化的方式表达了一种思想,即结果还上不存在。它同样能够帮助管理产生这个结果所需要的回调链。当从函数中返回时,Deferred对象承诺在某个时刻函数将产生一个结果。返回的Deferred对象中包含所有注册到事件上的回调引用,因此在函数间只需要传递这个对象即可,跟踪这个对象比单独管理所有的回调要简单的多。

Deferred对象包含一对回调链,一个是针对操作成功的回调,一个是针对操作失败的回调。初始状态下Deferred对象的两条链都为空。在事件处理的过程中,每个阶段都为其添加处理成功的回调和处理失败的回调。当一个异步结果到来时,Deferred对象就被“激活”,那么处理成功的回调和处理失败的回调就可以以合适的方式按照它们添加进来的顺序依次得到调用。

Deferred是Twisted对Callback的实现方式,Deferred非常灵活,代表了“推迟”。

当我们想执行一个异步操作时,我们可以使用Deferred来代替数据立即返回。Deferred的含义是:你想要的数据还没有到,不过你可以告诉我你接下来想要执行的操作,当我得到数据以后会调用你想要执行的函数。

Nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
for ( ;; ) {

if (ngx_exiting) {
if (ngx_event_no_timers_left() == NGX_OK) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
ngx_worker_process_exit(cycle);
}
}

ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");

ngx_process_events_and_timers(cycle);

if (ngx_terminate) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
ngx_worker_process_exit(cycle);
}

if (ngx_quit) {
ngx_quit = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
"gracefully shutting down");
ngx_setproctitle("worker process is shutting down");

if (!ngx_exiting) {
ngx_exiting = 1;
ngx_set_shutdown_timer(cycle);
ngx_close_listening_sockets(cycle);
ngx_close_idle_connections(cycle);
}
}

if (ngx_reopen) {
ngx_reopen = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
ngx_reopen_files(cycle, -1);
}
}

Proactor

  • Fully asynchronous
  • Can rely heavily on operating system functionality
  • Linux AIO, Windows IOCP

Reactor vs. Proactor

Reactor框架中用户定义的操作是在实际操作之前调用的。而Proactor框架中用户定义的操作是在实际操作之后调用的。

Reactor和Proactor都是并发编程中的设计模式。

Proactor用于异步IO,而Reactor用于同步IO。

An example will help you understand the difference between Reactor and Proactor. We will focus on the read operation here, as the write implementation is similar. Here’s a read in Reactor:

  • An event handler declares interest in I/O events that indicate readiness for read on a particular socket
  • The event demultiplexor waits for events
  • An event comes in and wakes-up the demultiplexor, and the demultiplexor calls the appropriate handler
  • The event handler performs the actual read operation, handles the data read, declares renewed interest in I/O events, and returns control to the dispatcher

By comparison, here is a read operation in Proactor (true async):

  • A handler initiates an asynchronous read operation (note: the OS must support asynchronous I/O). In this case, the handler does not care about I/O readiness events, but is instead registers interest in receiving completion events.
  • The event demultiplexor waits until the operation is completed
  • While the event demultiplexor waits, the OS executes the read operation in a parallel kernel thread, puts data into a user-defined buffer, and notifies the event demultiplexor that the read is complete
  • The event demultiplexor calls the appropriate handler;
  • The event handler handles the data from user defined buffer, starts a new asynchronous operation, and returns control to the event demultiplexor.

Standard/classic Reactor:

  • Step 1) wait for event (Reactor job)
  • Step 2) dispatch “Ready-to-Read” event to user handler ( Reactor job)
  • Step 3) read data (user handler job)
  • Step 4) process data ( user handler job)

Proposed emulated Proactor:

  • Step 1) wait for event (Proactor job)
  • Step 2) read data (now Proactor job)
  • Step 3) dispatch “Read-Completed” event to user handler (Proactor job)
  • Step 4) process data (user handler job)

Reactor实现了一个被动的事件分离和分发模型,服务等待请求事件的到来,再通过不受间断的同步处理事件,从而做出反应;

Proactor实现了一个主动的事件分离和分发模型;这种设计允许多个任务并发的执行,从而提高吞吐量;并可执行耗时长的任务(各个任务间互不影响)

适用场景

Reactor:同时接收多个服务请求,并且依次同步的处理它们的事件驱动程序; Proactor:异步接收和同时处理多个服务请求的事件驱动程序;

Others

Leader-Follower 模型,比如 Apache、PHP-FPM。

基于Epoll实现,比如Nginx、Node.js、Erlang、Golang。

异步编程为什么可以带来性能的提高?答案在于I/O。

程序运行所消耗的时间分为两部分:占用CPU进行计算的时间和等待I/O操作完成的时间。

同步和异步模型在前一种场景中的行为是相同的,区别在于后者,当用户进程执行I/O操作时,基于同步模型的程序被系统调用阻塞(Kernel,操作系统执行进程切换),直到I/O操作完成,驱动程序通知内核,用户进程得以从内核态返回;而基于异步模型的用户进程执行非阻塞的I/O系统调用,直接从内核态返回,但这时I/O的数据并没有准备好,用户进程可以继续进行其他的计算工作,当I/O数据准备好时,用户进程通过异步通知(aio或I/O多路复用)的机制获取数据执行数据处理操作。

异步编程利用等待I/O的时间去做其他工作,更加充分利用CPU资源。

(异步编程模型意味着你需要把原本连续的流程切分为多个不被阻塞的小代码块,然后以一种非常反人类的逻辑流程编写代码来换取计算机执行的高性能。)

多线程/多进程模型使得程序可以更加高效的利用多核的优势,和同步、异步模型并不冲突。

Reference

https://www.artima.com/articles/io_design_patternsP.html

https://stackoverflow.com/questions/9138294/what-is-th-difference-between-event-driven-model-and-reactor-pattern

https://likebeta.gitbooks.io/twisted-intro-cn/zh/

More than your eyes can see