当前位置: 首页 > 代码艺术, 网络翻译 > 正文

理解Reactor模式:线程模式和事件驱动模式

本文为技术翻译,原文出自:《Understanding Reactor Pattern: Thread-Based and Event-Driven》 。

首先,什么是Reactor模式呢?译者在此想结合自身理解说说个人见解。reactor的字面的翻译是“反应器设计模式”或者说“反应堆设计模式”,这里引用一下Wikipedia上的定义:“The reactor design pattern is an event handling pattern for handling service requests delivered concurrently by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to associated request handlers.”。根据这个描述,我们知道Reactor模式首先是基于事件驱动的,它有一个或多个并发输入源,有一个Service Handler,和有多个Request Handlers;这个Service Handler会同步的将输入的请求(Event)多路复用的分发给相应的Request Handler。可用下图来表达:

从结构上可以看出,这类似生产者消费者(Producter-Consumer)模式,即一个或多个生产者将事件放入Queue中,并由一个或多个消费者主动的从这个Queue中Poll事件来处理。在Reactor模式中,每当一个Event进入到ServiceHandler之后,该ServiceHandler会主动的根据不同的Event类型将其分发(Dispatch)给对应的Request Handler来处理。

在处理web请求时,通常有两种不同的体系结构处理方式:thread-based architecture(线程模式)、event-driven architecture(事件驱动模式)。

Thread-Based Architecture

实现多线程服务器最直观方式就一个线程处理一个连接(socket connection)的方式。它适用于需要避免线程与非线程安全库兼容的站点。 它还可以使用多进程的模式来隔离每个请求,因此如果单个请求出现问题也不会影响到其他请求。相对来说,进程的开销相对较大,其上下文切换慢,且内存消耗较高。因此,尽管使用多线程编程容易出错且难以调试,但每个连接线程都具有更好的可伸缩性。

为了调整线程数以获得最佳整体性能并避免线程创建/销毁开销,通常的做法是在阻塞队列和(消费者)线程池之前添加一个任务分发的调度线程。调度线程在套接字上阻塞新连接,并将它们分发给阻塞队列处理。超出队列限度的连接将会被丢弃,但已接受的连接的可以继续连接使用。线程池轮询队列以获取进入到队列中的请求,然后处理并响应。 不足的是,连接和线程之间始终保持一对一的关系,如果是一直处于Keep-Alive状态的长连接将会导致大量工作线程在空闲状态下等待,例如,文件系统访问,网络等。此外,成百上千的连接还可能会导致并发线程浪费大量内存的堆栈空间。

Event-Driven Architecture

事件驱动模式可以将线程与连接分开,连接仅使用线程来处理特定的回调或处理线程上的事件。事件驱动的体系结构由事件生产者和事件消费者组成,生产者是事件的来源,它只知道事件已经产生,而消费者则需要知道事件发生的实体。它们可能参与处理事件,或者它们可能只是受事件的影响。

The Reactor Pattern

Reactor模式是事件驱动架构的一种实现技术。简单来说,它使用单个线程事件循环监听产生事件的fd,并将它们分发到相应的处理者或回调。只要需要注册事件的处理handler或回调来处理它们即可,而不需要阻塞I/O来等待事件,如新的接入连接,准备好读取,准备好写入等。这些处理handler/回调可以在多核环境中使用线程池。这种模式将程序代码模块化与可复用反应器解耦,从而实现并发请求与事件处理分离。

Reactor模式的架构中有两个重要的参与者:

1.Reactor

Reactor在一个单独的线程中运行,其任务是通过将工作给对应的处理handler来对IO事件做出响应。它就像公司中的电话接线员,将接听来自客户的电话并将线路转移到适当的联系人。

2.Handlers

处理程序执行对应I/O事件实际需要业务逻辑,这类似于客户想要与之交谈的公司中的实际人员。Reactor通过调度对应的处理程序来响应I/O事件,这个处理程序执行是非阻塞操作。

The Intent of the Reactor Pattern

Reactor架构模式允许基于事件驱动的应用程序复用和分发从一个或多个客户端发送到应用程序的服务请求。一个Reactor模型将持续监听事件,并在事件被触发后通知相应的事件处理程序处理它。Reactor Pattern是一种设计模式,用于同步多路分用和处理事件到达时的顺序,它接收来自多个客户端的并发消息/请求/连接,并使用事件处理程序按顺序处理这些消息。Reactor设计模式的目的是可以避免为每个消息/请求/连接创建单独的线程。

避免这个问题的经典问题是:C10K问题

总结:当服务端必须处理超过10,000个并发客户端连接,并且线程无法使用Tomcat,Glassfish,JBoss或HttpClient来扩展连接时,使用Reactor模式的应用程序只需要使用一个线程来处理高并发事件。

标准的Reactor模式允许带有同步事件的前导应用程序,同时保持单线程的简单性。同步多路分用器(demultiplexer)类似一个具有输入和多个输出的电路,当就相当于你向多个设备之一发送信号时的电路。这听起来类似于解码器的描述,但是它可用于多个设备间的选择,而同步多路分用器则用于在多个设备之间发送信号。

Reactor模式允许使用单个线程来有效地处理多个任务。 Reactor还管理一组事件处理程序(EventHandler),调用执行任务时,它会与可用的处理程序(Handles)连接并使其处于活动状态。

The Cycle of Events:

  1. 查找出所有处于活动状态和未锁定状态的处理程序,或已将其交给调度分发程序实现;
  2. 依次执行这些处理程序中的每一个,直到完成,或者直到它们被阻止。已完成的处理程序将停用,并允许事件继续循环监听;
  3. 重复第一步(1)。

因为Node.jsVert.xReactive ExtensionsJettyNgnix等都使用了Reactor模式。因此如果你喜欢区分设计模式并想知道其背后的工作效果,那么注意这种模式是很重要的。

译者说:

在翻译这篇文章的过程中,很多地方直译不够准确完美。译者也搜索了不少关于Reactor设计模式的论文、技术文章,发现Reactor还有更深的理论基础是本文无法详尽表达的。鉴于保证原文的主观表意,这里并未做更深入的探讨,译者将在后面深入分析Reactor设计模式,敬请期待。



这篇博文由 s0nnet 于2018年07月26日发表在 代码艺术, 网络翻译 分类下, 通告目前不可用,你可以至底部留下评论。
如无特别说明,独木の白帆发表的文章均为原创,欢迎大家转载,转载请注明: 理解Reactor模式:线程模式和事件驱动模式 | 独木の白帆
关键字:

理解Reactor模式:线程模式和事件驱动模式:等您坐沙发呢!

发表评论

快捷键:Ctrl+Enter