Reactor模式详解

  • 时间:
  • 浏览:1

归功与Netty和Java NIO对Reactor的宣传,本文慕名而学习的Reactor模式,因而是因为默认Reactor具有非常优秀的性能,然而慕名归慕名,到这里,我还是要不得不问另一方Reactor模式的好所处哪里?即怎么要使用这人 Reactor模式?在Reactor An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events中是越来越说的:

1. Client发送log到Logging server。2. InitiationDispatcher监测到相应的Handle蕴藏事件所处,返回阻塞守候,根据返回的Handle找到LoggingHandler,并回调LoggingHandler中的handle_event()最好的办法 。3. LoggingHandler中的handle_event()最好的办法 中读取Handle中的log信息。4. 将接收到的log写入到日志文件、数据库等设备中。3.4步骤循环直到当前日志补救完成。5. 返回到InitiationDispatcher守候下一次日志写请求。Reactor An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events有对Reactor模式的C++的实现版本,多年不让C++,因而略过。 

Separation of concerns: The Reactor pattern decouples application-independent demultiplexing and dispatching mechanisms from application-specific hook method functionality. The application-independent mechanisms become reusable components that know how to demultiplex events and dispatch the appropriate hook methods defined by Event Handlers. In contrast, the application-specific functionality in a hook method knows how to perform a particular type of service.

要回答这人 大问题,首先当然是求助Google或Wikipedia,其中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模式首先是事件驱动的,有1个 或多个并发输入源,有1个 Service Handler,有多个Request Handlers;这人 Service Handler会同步的将输入的请求(Event)多路复用的整理给相应的Request Handler。是因为用图来表达:

从特征上,这怪怪的类似生产者消费者模式,即有1个 或多个生产者将事件装下 去 1个 Queue中,而1个 或多个消费者主动的从这人 Queue中Poll事件来补救;而Reactor模式则并越来越Queue来做缓冲,每当1个 Event输入到Service Handler事先 ,该Service Handler会主动的根据不同的Event类型将其整理给对应的Request Handler来补救。更学术的,这篇文章(Reactor An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events)上说:“The Reactor design pattern handles service requests that are delivered concurrently to an application by one or more clients. Each service in an application may consistent of several methods and is represented by a separate event handler that is responsible for dispatching service-specific requests. Dispatching of event handlers is performed by an initiation dispatcher, which manages the registered event handlers. Demultiplexing of service requests is performed by a synchronous event demultiplexer. Also known as Dispatcher, Notifier”。这段描述和Wikipedia上的描述类似,有多个输入源,有多个不同的EventHandler(RequestHandler)来补救不同的请求,Initiation Dispatcher用于管理EventHander,EventHandler首先要注册到Initiation Dispatcher中,假如Initiation Dispatcher根据输入的Event整理给注册的EventHandler;然而Initiation Dispatcher不须监听Event的到来,这人 工作交给Synchronous Event Demultiplexer来补救。

在Java的NIO中,对Reactor模式是否缝的支持,即使用Selector类封装了操作系统提供的Synchronous Event Demultiplexer功能。这人 Doug Lea是因为在Scalable IO In Java蕴藏非常深入的解释了,因而不再赘述,另外这篇文章对Doug Lea的Scalable IO In Java有全都简单解释,要花费它的代码格式比Doug Lea的PPT要整洁全都。

都还可以 指出的是,不同这里使用InitiationDispatcher来管理EventHandler,在Doug Lea的版本中使用SelectionKey中的Attachment来存储对应的EventHandler,因而不都还可以 注册EventHandler这人 步骤,是因为设置Attachment也不这里的注册。假如在这篇文章中,Doug Lea从单系统应用应用程序的Reactor、Acceptor、Handler实现这人 模式出发;演化为将Handler中的补救逻辑多系统应用应用程序化,实现类似Proactor模式,此时所有的IO操作还是单系统应用应用程序的,因而再演化出1个 Main Reactor来补救CONNECT事件(Acceptor),而多个Sub Reactor来补救READ、WRITE等事件(Handler),什么Sub Reactor都还可以分别再另一方的系统应用应用程序中执行,从而IO操作也多系统应用应用程序化。这人 最后1个 模型正是Netty中使用的模型。假如在Reactor An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events的9.5 Determine the Number of Initiation Dispatchers in an Application中总要相应的描述。

Provides coarse-grained concurrency control: The Reactor pattern serializes the invocation of event handlers at the level of event demultiplexing and dispatching within a process or thread. Serialization at the Initiation Dispatcher level often eliminates the need for more complicated synchronization or locking within an application process.

Reactor模式的缺点貌似也是显而易见的:

1. 相比传统的简单模型,Reactor增加了一定的繁杂性,因而有一定的门槛,假如不易于调试。2. Reactor模式都还可以 底层的Synchronous Event Demultiplexer支持,比如Java中的Selector支持,操作系统的select系统调用支持,是因为要另一方实现Synchronous Event Demultiplexer是因为不让有越来越高效。3. Reactor模式在IO读写数据时还是在同1个 系统应用应用程序中实现的,即使使用多个Reactor机制的请况下,什么共享1个 Reactor的Channel是因为再次跳出1个 长时间的数据读写,会影响这人 Reactor中全都Channel的相应时间,比如在大文件传输时,IO操作就会影响全都Client的相应时间,因而对这人 操作,使用传统的Thread-Per-Connection或许是1个 更好的选着 ,或则此时使用Proactor模式。

1. Logging Server注册LoggingAcceptor到InitiationDispatcher。2. Logging Server调用InitiationDispatcher的handle_events()最好的办法 启动。3. InitiationDispatcher结构调用select()最好的办法 (Synchronous Event Demultiplexer),阻塞守候Client连接。4. Client连接到Logging Server。5. InitiationDisptcher中的select()最好的办法 返回,并通知LoggingAcceptor有新的连接到来。 6. LoggingAcceptor调用accept最好的办法 accept这人 新连接。7. LoggingAcceptor创建新的LoggingHandler。8. 新的LoggingHandler注册到InitiationDispatcher中(一齐也注册到Synchonous Event Demultiplexer中),守候Client发起写log请求。Client向Logging Server写Log

Efficiency: Threading may lead to poor performance due to context switching, synchronization, and data movement [2];

对于性能,它人太好也不第全都关于Efficiency的描述,即系统应用应用程序的切换、同步、数据的移动会引起性能大问题。也也不说从性能的深度上,它最大的提升也不减少了性能的使用,即不都还可以 每个Client对应1个 系统应用应用程序。我的理解,全都业务逻辑补救全都 事先 也会用到相同的系统应用应用程序,IO读写操作相对CPU的操作还是要慢全都 ,即使Reactor机制中每次读写是因为能保证非阻塞读写,这都还可以不能减少全都系统应用应用程序的使用,假如这减少的系统应用应用程序使用对性能有越来越大的影响吗?答案貌似是肯定的,这篇论文(SEDA: Staged Event-Driven Architecture - An Architecture for Well-Conditioned, Scalable Internet Service)对随着系统应用应用程序的增长带来性能降低做了1个 统计:

在这人 统计中,每个系统应用应用程序从磁盘中读8KB数据,每个系统应用应用程序读同1个 文件,因而数据三种是缓所处操作系统结构的,即减少IO的影响;所有系统应用应用程序是事先 分配的,不让有系统应用应用程序启动的影响;所有任务在测试结构产生,因而不让有网络的影响。该统计数据运行环境:Linux 2.2.14,2GB内存,4-way 30MHz Pentium III。从图中都还可以看出,随着系统应用应用程序的增长,吞吐量在系统应用应用程序数为8个左右的事先 事先 刚开使线性下降,假如到61个 事先 而太快下降,其相应事件也在系统应用应用程序达到256个后指数上升。即1+1<2,是因为系统应用应用程序切换、同步、数据移动会有性能损失,系统应用应用程序数增加到一定数量时,这人 性能影响效果会更加明显。对于这点,还都还可以参考C10K Problem,用以描述一齐有10K个Client发起连接的大问题,到2010年的事先 是因为再次跳出10M Problem了。当然总要人说:Threads are expensive are no longer valid.在不久的将来是因为又会所处不同的变化,因怎么儿 变化正在、是因为所处着?越来越做过比较仔细的测试,因而不敢随便断言什么,然而另一方观点,即使系统应用应用程序变的影响并越来越事先 越来越大,使用Reactor模式,甚至时SEDA模式来减少系统应用应用程序的使用,再换成全都解耦、模块化、提升复用性等优点,还是值得使用的。

简单描述一下Reactor各个模块之间的交互流程,先从序列图事先 刚开使:

1. 初始化InitiationDispatcher,并初始化1个 Handle到EventHandler的Map。2. 注册EventHandler到InitiationDispatcher中,每个EventHandler蕴藏对相应Handle的引用,从而建立Handle到EventHandler的映射(Map)。3. 调用InitiationDispatcher的handle_events()最好的办法 以启动Event Loop。在Event Loop中,调用select()最好的办法 (Synchronous Event Demultiplexer)阻塞守候Event所处。4. 当某个或全都Handle的Event所处后,select()最好的办法 返回,InitiationDispatcher根据返回的Handle找到注册的EventHandler,并回调该EventHandler的handle_events()最好的办法 。5. 在EventHandler的handle_events()最好的办法 中还都还可以向InitiationDispatcher中注册新的Eventhandler,比如对AcceptorEventHandler来,当有新的client连接时,它会产生新的EventHandler以补救新的连接,并注册到InitiationDispatcher中。

原文链接:[http://wely.iteye.com/blog/2329842]

Reactor Pattern WikiPedia

Reactor An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous EventsScalable IO In JavaC10K Problem WikiPedia

Improve modularity, reusability, and configurability of event-driven applications: The pattern decouples application functionality into separate classes. For instance, there are two separate classes in the logging server: one for establishing connections and another for receiving and processing logging records. This decoupling enables the reuse of the connection establishment class for different types of connection-oriented services (such as file transfer, remote login, and video-on-demand). Therefore, modifying or extending the functionality of the logging server only affects the implementation of the logging handler class.

4.0 removes event object creation almost completely by replacing the event objects with strongly typed method invocations. 3.x had catch-all event handler methods such as handleUpstream() and handleDownstream(), but this is not the case anymore. Every event type has its own handler method now:

在补救了什么是Reactor模式后,大伙来看看Reactor模式是由什么模块构成。图是三种比较简洁形象的表现最好的办法 ,因而先上一张图来表达各个模块的名称和大伙之间的关系:

Handle:即操作系统中的句柄,是对资源在操作系统层面上的三种抽象,它都还可以是打开的文件、1个 连接(Socket)、Timer等。是因为Reactor模式一般使用在网络编程中,因而这里一般指Socket Handle,即1个 网络连接(Connection,在Java NIO中的Channel)。这人 Channel注册到Synchronous Event Demultiplexer中,以监听Handle中所处的事件,对ServerSocketChannnel都还可以是CONNECT事件,对SocketChannel都还可以是READ、WRITE、CLOSE事件等。Synchronous Event Demultiplexer:阻塞守候一系列的Handle中的事件到来,是因为阻塞守候返回,即表示在返回的Handle中都还可以不阻塞的执行返回的事件类型。这人 模块一般使用操作系统的select来实现。在Java NIO中用Selector来封装,当Selector.select()返回时,都还可以调用Selector的selectedKeys()最好的办法 获取Set<SelectionKey>,1个 SelectionKey表达1个 有事件所处的Channel以及该Channel上的事件类型。上图的“Synchronous Event Demultiplexer ---notifies--> Handle”的流程是因为是对的,那结构实现应该是select()最好的办法 在事件到来总要先设置Handle的请况,假如返回。不了解结构实现机制,因而保留原图。Initiation Dispatcher:用于管理Event Handler,即EventHandler的容器,用以注册、移除EventHandler等;另外,它还作为Reactor模式的入口调用Synchronous Event Demultiplexer的select最好的办法 以阻塞守候事件返回,当阻塞守候返回时,根据事件所处的Handle将其整理给对应的Event Handler补救,即回调EventHandler中的handle_event()最好的办法 。Event Handler:定义事件补救最好的办法 :handle_event(),以供InitiationDispatcher回调使用。Concrete Event Handler:事件EventHandler接口,实现特定事件补救逻辑。

什么貌似是全都 模式的共性:解耦、提升复用性、模块化、可移植性、事件驱动、细力度的并发控制等,因而不须能很好的说明什么,怪怪的是它鼓吹的对性能的提升,这里并越来越体现出来。当然在这篇文章的开头有描述过另三种直观的实现:Thread-Per-Connection,即传统的实现,提到了这人 传统实现的以下大问题:

Improves application portability: The Initiation Dispatcher’s interface can be reused independently of the OS system calls that perform event demultiplexing. These system calls detect and report the occurrence of one or more events that may occur simultaneously on multiple sources of events. Common sources of events may in- clude I/O handles, timers, and synchronization objects. On UNIX platforms, the event demultiplexing system calls are called select and poll [1]. In the Win32 API [16], theWaitForMultipleObjects system call performs event demultiplexing.

第一次听到Reactor模式是三年前的某个晚上,1个 室友老要 跑过来问我什么是Reactor模式?我上网查了一下,全都 人总要给出NIO中的 Selector的例子,假如也不NIO里Selector多路复用模型,也不给它起了1个 比较fancy的名字而已,人太好它引入了EventLoop概 念,这对我来说是新的概念,假如代码实现却是一样的,因而我并越来越很在意这人 模式。然而最近事先 刚开使读Netty源码,而Reactor模式是全都 介绍Netty的文章中被大肆宣传的模式,因而我再次问另一方,什么是Reactor模式?本文也不对这人 大问题关于我的全都理解和尝试着来解答。

Programming simplicity: Threading may require complex concurrency control schemes;

Reactor An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events中,老要 以Logging Server来分析Reactor模式,这人 Logging Server的实现完全遵循这里对Reactor描述,因而装下 去 这里以做参考。Logging Server中的Reactor模式实现分1个 主次:Client连接到Logging Server和Client向Logging Server写Log。因而对它的描述分成这1个 步骤。

Client连接到Logging Server

对EventHandler的定义有三种设计思路:single-method设计和multi-method设计:

A single-method interface:它将Event封装成1个 Event Object,EventHandler只定义1个 handle_event(Event event)最好的办法 。这人 设计的好处是有有助于于扩展,可事先 来方便的换成新的Event类型,然而在子类的实现中,都还可以 判断不同的Event类型而再次扩展成 不同的补救最好的办法 ,从这人 深度上来说,它又不有有助于于扩展。另外在Netty3的使用过程中,是因为它不停的创建ChannelEvent类,因而会引起GC的不稳定。A multi-method interface:这人 设计是将不同的Event类型在 EventHandler中定义相应的最好的办法 。这人 设计也不Netty4中使用的策略,其中1个 目的是补救ChannelEvent创建引起的GC不稳定, 另外1个 好处是它都还可以补救在EventHandler实现时判断不同的Event类型而有不同的实现,然而这人 设计会给扩展新的Event类型时带来非常 大的麻烦,是因为它都还可以 该接口。关于Netty4对Netty3的改进都还可以参考这里