一般来说,网络应用系统服务器的实现,我们从设计模式的角度看,有两种设计方案可供选择:
Reactor服务器,或者Proactor服务器。无论是Reactor,或者Proactor,
都是基于事件驱动的架构设计(Event-driven architecture),
它们的核心是思想是:分离网络事件的监视,驱动与事物本身的逻辑处理。我们能看到的是:对任何的网络应用而言,其对网络事件的监视,处理往往是大同小异的,是可以分离出来作为可复用组件而存在的。
对所有这些网络应用,所不用的是对网络请求的逻辑处理。不同于Proactor,
或者Reactor,一般的设计方法是:
![](http://p.blog.csdn.net/images/p_blog_csdn_net/sherrylso/%E6%9C%AA%E5%91%BD%E5%90%8D.jpg)
而Proactor, 或者Reactor,设计的精髓是:
![](http://p.blog.csdn.net/images/p_blog_csdn_net/sherrylso/%E6%9C%AA%E5%91%BD%E5%90%8D2.jpg)
别看这一点小小的变化,这是所谓的Hollywood Principle: 'Don't call us, we'll call
you'。这是Framework设计者惯用的设计技巧,依赖倒转(The Dependency-Inversion Principle):
a.
高层模块不应该依赖于低层模块,应当依赖于抽象。
b. 抽象不应该依赖于细节,细节依赖于抽象。
言归正传,我们来具体谈谈Reatctor and
Proactor.
一
、Reactor服务器实现。
在Reactor的实现可分为两层:
a.Demultiplexing/Dispatching层:独立于应用逻辑层,捕获,分发网络请求给具体的网络请求处理器。
在这里注意的是,Demultiplexing和Dispatching是两个不同的概念,Demultiplexing指的是网络请求的侦听,及其侦听以后寻找适合的处理器。Dispatching指的是对处理器的回调。
b.应用逻辑层组件:主要处理与应用相关的事物处理。
其基本的结构图为
![](http://p.blog.csdn.net/images/p_blog_csdn_net/sherrylso/%E6%9C%AA%E5%91%BD%E5%90%8D11.jpg)
其各个组成模块的功能为:
--〉句柄:
调用操作系统API获得,用于identify事件源,如:网络连接,打开的文件。
--〉同步事件分离器:操作系统功能,通过API提供,典型的如:
select调用,如windows平台上的WaitForMultileObj
--〉事件处理器:这是Demultiplexing/Dispatching层定义的抽象借口,是Demultiplexing/Dispatching层与应用逻辑层之间的协议约定。应用逻辑层依赖于该抽象借口。
--〉具体事件处理器:事件处理器的具体实现,一般来说,由应用逻辑层组件实现。
--〉Reactor:提供接口给应用逻辑层,注册或者反注册事件处理器,运行应用的事件循环(反复调用同步事件分离器),驱动Demultiplexing/Dispatching层。
二
、Proactor服务器实现。
在Proactor的实现也分为两层:
a.Demultiplexing/Dispatching层:独立于应用逻辑层,捕获,分发网络请求给具体的网络请求处理器,不同于Reactor的是,这一层的策略处理
基于异步操作,从实现的复杂度上,要复杂于Reactor。
b.应用逻辑层组件:主要处理与应用相关的事物处理。
Proactor基本的结构图为
![](http://p.blog.csdn.net/images/p_blog_csdn_net/sherrylso/%E6%9C%AA%E5%91%BD%E5%90%8D12.jpg)
--〉句柄:
调用操作系统API获得,用于identify事件源,如:网络连接,打开的文件。与Reactor不同的是,由于Proactor操作是异步的,所以,与每一个异步IO操作项关联的,有一个完成事件(Completion
Event),当一个异步的IO操作执行完成后,一个操作系统的IO子系统会产生一个完成事件。
--〉异步操作:一个异步操作可以是一个服务请求的具体实现,如从连接的socket上读入数据,或者写入数据到本地磁盘。异步操作的发起执行,并不会由于慢速的IO而阻塞调用线程,调用线程在发起异步操作之后,可以去做别的事情。这是Proactor能增大服务器处理吞吐量的关键所在。我们付出的代价是,开发的复杂度要比Reactor。
--〉完成
事件处理器:Demultiplexing/Dispatching层定义的抽象借口。由应用逻辑层组件继承来实现这些接口
函数。
--〉具体完成事件处理器:事件处理器的具体实现。
--〉异步操作处理器:由native的操作系统实现。当一个异步操作完成执行时,异步操作处理器会产生一个完成事件,然后把这个完成事件插入到完成事件队列。
--〉完成事件队列:用于保存异步操作完成事件的队列。在windows平台上,就是我们所说的完成端口。
--〉异步事件分离器:
操作系统功能,通过API提供。在windows平台上,就是GetQueuedCompletionStatus()。异步事件分离器等待在完成事件队列上,当有完成事件被插入到完成事件队列,异步事件分离器会从完成事件队列取出该完成事件,返回给调用者。
--〉Proactor:运行应用的事件循环(反复调用异步操作处理器),驱动Demultiplexing/Dispatching层。
--〉发起者:用于发起异步IO操作。
三
、选择?是Reactor,还是Proactor。
在我们实现我们的服务器,是选择Reactor,还是Proactor,个人认为需要考虑的因素是:
1)对于Reactor而言,好的地方是开发的复杂度小于Proactor,但缺点同样明显:
第一,基于Reactor实现的服务器可扩展性不如Proactor,这是由其本质决定的。在wiondows平台上,无论是
Select,或者是WaitForMultiObj,其等待的最大handle数有一个最大的上限值(默认值是64)。
第二,基于Reactor实现的服务器服务请求处理的吞吐量不如Proactor好。特别是当对客户端的请求处理需要更长的时间时,在Reactor的Demultiplexing/Dispatching层,由于Demultiplexing和Dispatching被顺序化处理,这样的话,很容易成为服务器性能的瓶颈。
2)对于Proactor,如果你需要一个高性能的,高吞吐量的,并发服务器,Proactor绝对是首选的解决方案,它的问题主要在开发的难度比较大。
不过,总的来说,我们可以看到,基于模式设计的Reactor和Proactor,都很好地把Application-Specific的处理逻辑从网络,IO事件的Demultiplexing/Dispatching中分离出来,对应用程序而言,由于这样的松耦合关系,我们的应用完全可以在Reactor和Proactor中自由切换,而不需要修改任何的代码。、
四
、后记。
ACE Framework,对Reactor和Proactor设计模式给出了经典的实现。请参考:
http://www.cs.wustl.edu/~schmidt/
个人觉得是很好的学习材料。