摘 要:介绍了ACE反应器框架的核心设计与实现,揭示了该框架对事件实施多路分解和分派的机制。
关键词:ACE反应器设计模式框架
在分布式系统、特别是服务器的事件驱动型应用中,必须随时准备同时处理多个服务请求。许多传统的应用程序在处理诸如网络连接这样的多个I/O端口服务时,往往借助于多进程模型或多线程模型。这种方法在需要同时处理多个网络连接的服务器程序中相当流行。但是,在一些系统中,进程和线程的创建开销和维护代价非常大。其不足主要表现在以下方面:
(1)由于CPU之间的上下文切换、同步和数据移动,使得线程方法效率不高。
(2)线程方法不适用于所有的操作系统,并且不是所有的操作系统都提供可移植的线程语义。
(3)相似的事件处理代码在每一个新的工程中,由开发者面对许多复杂细节的情况下被重复地开发出来,并且这些程序在移植时困难重重。
(4)在制订进程或线程策略进行并发服务器的优化时,如果不考虑像CPU的数目这样的可用资源,实际上往往降低了程序的执行效率。
本文描述ACE(自适配通信环境)反应器框架的设计和实现。该框架可以有效克服上述不足。它实现了反应器(Reactor)模式[1],该模式将事件多路分解和分派机制从服务中对指示事件的与应用有关的处理分离了出去。应用的每个服务可由一个或多个方法组成,并由一个单独的事件处理器代表;事件处理器负责分派服务特有的请求。在ACE的反应器模式的实现中,事件处理器分派由ACE_Reactor对象完成。
1 ACE反应器框架类
这一部分简要介绍反应器框架(ACE_Reactor)的类。图1显示了该框架中相关类之间最为重要的关系。
(1)ACE_Time_Value类:主要用于ACE Reactor I/O超时和定时器设置。
(2)ACE_Event_Handler类:抽象类,其接口提供的挂钩方法是ACE_Reactor回调的目标。大多数通过ACE开发的应用事件处理器都是该类的后代。
(3)ACE_Timer_Queue类:抽象类,定义定时器队列的能力和接口。
(4)ACE_Reactor类:提供一个接口,用来在Reactor框架中管理事件处理器登记,并执行事件循环来驱动事件检测、多路分解和分派。
依照反应器模式,这些类扮演了事件基础设施层和应用层二种角色。其中,担当应用层角色的是ACE_Event_Handle类。
2 ACE反应器框架的核心实现
实现ACE反应器框架的关键是运用反应器(Reactor)模式。反应器模式中有五个主要的组成部分:(1)操作系统提供的句柄:用于标识网络连接或打开的文件之类的事件源,事件源产生指示事件并对其进行排队。(2)同步事件多路分解器:是一个函数,如select()等,它是事件多路分解器的核心。(3)事件处理程序:定义一个或多个钩子方法组成的接口。(4)具体事件处理程序:从事件处理程序接口继承,并实现应用所特定的服务。(5)反应器:定义了一个接口,允许应用程序登记或删除事件处理程序及其相关的句柄,并运行应用程序的事件循环。反应器使用同步事件多路分解器等待在句柄集上发生指示事件。
应用程序开发者只需要负责具体事件处理程序,并在反应器上予以注册,应用程序就可以简单地重用反应器的多路分解和分配机制了。反应器模式引入的结构实现回调的方法是:反应器等待指示事件,多路分解这些事件给具体事件处理程序,然后向具体事件处理程序分派相应的钩子方法。下面介绍反应器框架的实现。
2.1 定义事件处理程序对象
在面向对象应用中,将事件处理程序与句柄结合起来的方法是建立一个事件处理对象。ACE反应器框架定义了不同类型的各种对象及相应的钩子方法,这种由具体事件处理对象来分派的多接口策略更具可扩展性。下面简化的C++抽象基类即是在ACE反应器框架中用来产生这种类型的对象:
class ACE_Event_Handler{
public://由反应器分派的用以处理各种具体事件处理程序的钩子方法
virtual int handle_input(HANDLE handle)=0;//输入事件
virtual int handle_outpur(HANDLE handle)=0;//输出事件
virtual int handle_timeout(const ACE_Time_Value&)=0;
//超时事件
virtual HANDLE get_handle( ) const=0;}//用于返回I/O句柄的钩子方法
2.2 定义反应器接口
下面是一个简化的ACE_Reactor类,它是ACE反应器框架中实现反应器模式的关键类。应用程序使用该反应器接口登记或删除事件处理程序及其相关句柄,并调用应用程序的事件循环。通常用单件[5]访问反应器接口,因为一个应用程序中有一个反应器就够了。
class ACE_Reactor {
public:
virtual int register_handler(ACE_Event_Handler*event_
handler,ACE_Reactor_Mask masks);
//登记具体事件处理器
virtual int register_handler(ACE_Sig_Set sigset,ACE_Event_
Handler*event_handler,ACE_Reactor_Mask masks);
//该事件处理器与信号处理有关
virtual int schedule_timer(ACE_Event_Handler*event_
handler,ACE_Time_Value time);//登记一个事件处理器
//它将在用户规定的时间后被执行
virtual int remove_handler(HANDLE h,ACE_Reactor_
Mask masks);//移除具体事件处理器
void handle_events(ACE_Time_Value*timeout=0);
//启动反应器的事件循环处理
static Reactor*instance( );//返回反应器单体实例
private:
ACE_Reactor_Impl implementation;}//反应器的具体实现
在上面的类中,ACE_Reactor_Mask是个自定义类型:
typedef unsigned long ACE_Reactor_Mask
它一般取以下枚举类型的值,用来标志不同类型的事件:
enum{
READ_MASK=(1 << 0),
WRITE_MASK=(1 << 1),
EXCEPT_MASK=(1 <<2 )
……}
ACE_Reactor类是应用程序用以访问ACE反应器框架的公共接口。桥接模式[5]使ACE_Reactor接口与它的ACE_Reactor_Impl子类实现耦合。在不同OS平台上,该子类的实现也不相同。但是,ACE_Reactor接口提供的方法的名字和总的功能保持不变。这种统一性源于ACE_Reactor设计的模块性,该设计还增强了反应器的可重用性、可移植性和可维护性。
2.3 反应器中多路分解和分派的实现
通常,除了调用同步事件多路分解器等待句柄集发生指示事件外,反应器实现还要维护一个多路分解表。该表是个管理者,包含一个格式为<句柄,事件处理程序,指示事件类型>的三元组,这使得所激活的句柄与激活该句柄的指示事件类型、所激活的句柄与该句柄所关联的事件处理器三者之间清晰地联系在一起。可用Linux操作系统为例来阐述这一点,因为在Linux中,I/O句柄是连续的整数,这使得句柄值成为多路分解表数组的索引。
在具体实现中,ACE反应器类的私有部分有一个包含上述多路分解表的对象handler_rep_,该对象存放着一个实现句柄到具体事件处理器映射的表,它是下面所示类的一个实例:
class ACE_Select_Reactor_Handler_Repository{
public://寻找与handle相关联的具体事件处理器
ACE_Event_Handler*find(ACE_HANDLE handle,size_t
*index_p=0);
//使得具体事件的句柄、指示标志绑定在一起
Int bind(ACE_HANDLE,ACE_Event_Handler*,ACE_
Reactor_Mask):
size_t size(void) const;//返回表中所绑定的具体事件处理器的个数
private://盛装一个事件处理器ACE_Event_Handler和其相关
//联的ACE_HANDLE句柄
ACE_Event_Tuple*event_handler-;}
class ACE_Event_Tuple{
public:
ACE_HANDLE handle_;
ACE_Event_Handler*event_handler_;}
当具体事件处理器调用register_handler( )方法向Reactor类登记时,将调用方法handler_rep_.bind (handle,event_handler,mask),把这个“三元组”置于handler_rep_对象中。
在ACE中,用包装器外观类ACE_Handle_Set来封装句柄集,用一个实现了迭代模式的类ACE_Handle_Set_
Iterator来迭代该句柄集中的句柄。一旦反应器进入主入口点方法handle_events( ),应用程序将利用它反应性地实现事件循环。当有指示事件发生时,反应器多路分解与分派(即回调)的方式为:该框架把反应器应处理的事件分成三种不同的类型,其中一类为I/O事件,由方法dispatch_io_
handlers( )加以处理。下面以处理常见应用中的I/O事件为例说明反应器的多路分解和分派的实现。
先定义一个函数指针:
typedef int (ACE_Event_Handler∷?鄢ACE_EH_PTMF) (ACE_HANDLE);
此函数指针是实现具体服务分派的基础,因为反应器通过函数指针决定了应调用handle_?鄢( )方法中的哪一种。
再定义一个函数:
ACE_Reactor_impl∷dispatch_io_set( ACE_Handle_Set&
dispatch_mask,ACE_EH_PTMF callback)
{ ACE_Handle_Set_Iterator handle_iter(dispatch_mask);
//对特定句柄集中的句柄进行迭代
While(handle=handle_iter( )!=ACE_INVALID_HANDLE)
{ ACE_Event_Handler //找出被激活句柄相关联
//的事件处理器
event_handler=this->handler_rep_.find(handle);
//利用函数指针对反映器进行回调
(event_handler->*callback)(handle);}
}
上述函数对dispatch_mask句柄集中的句柄执行循环检测。一旦某句柄被激活,即找出该句柄所关联的ACE_Event_Handler对象,然后执行该对象所分派的方法,而具体方法由callback传递。
3 运用反应器框架的示例
以常见的登录服务器为例。登录服务器使用由两个具体事件处理程序,即登录接受器和登录连接器实现的单件反应器。下面的代码框架实现了登录服务器示例中的具体事件处理程序。其中:My_Accept_Handler类(登录接受器)用来被动地建立连接,My_Input_Handler类(登录连接器)提供与应用有关的服务。
class My_Input_Handler:public ACE_Event_Handler{
public:
int handle_input(ACE_HANDLE){ //回调,以处理任何到来的连接
……具体代码略……}
private:
ACE_SOCK_Stream peer_;}//流对象,用于读写
class My_Accept_Handler:public ACE_Event_Handler{
public:
My_Accept_Handler(ACE_Addr &addr){this->open(addr);}
int open(ACE_Addr &addr) {//打开接受器,监听客户连接
peer_acceptor.open(addr);}
int handle_input(ACE_HANDLE handle){
//重载handle_input( )方法
……代码略,客户请求连接,则建立一个处理器来处理这个连接……}
private:
ACE_SOCK_Accepto peer_acceptor;} //用以被动接受连接的接受器
int main(int argc,char*argv[]){
ACE_INET_Addr addr(PORT_NO);//建立一个用来接受连接的地址
//建立一个接受事件处理器用以自动侦听客户连接
My_Accept_Handler*eh=new My_Accept_Handler(addr);
//向Reactor登记,以使在有连接请求时实现回调
ACE_Reactor∷instance( )->register_handler(eh,
ACE_Event_Handler∷ACCEPT_MASK);
while(1) //运行事件循环
ACE_Reactor∷instance()->handle_events();}
4 总 结
本文对实现ACE反应器框架的核心源码进行了分析,揭示了该框架使多路分解和分派机制与应用定义的事件处理策略相分离的方法。最后的实例表明,通过封装许多复杂的功能,该框架可简洁、正确、可移植和高效地进行事件驱动型网络化应用的开发,从而使网络化开发者能够专注于应用所特有的服务。
参考文献
1 Schmidt D C,Stal M,Rohnert H et al.Pattern-Oriented Software Architecture:Patterns for Concurrent and Networked Objects.West Sussex:Wiley&Sons,2000
2 Schmidt D C,Huston S D.C++ Network Programming,Volume 2:Systematic Reuse with ACE and Frameworks. Massachusetts:Addison-Wesley,2002
3 Schmidt D C.Reactor:An Object Behavioral Pattern for Demultiplexing and Despatching Handles for Synchronous Events.http://www.cs.wustl.edu/~doc/pspdfs/Reactor.pdf,2004-12-05
4 Schmidt D C,Pyarali I.The Design and Use of the ACE Reactor:An Objiect-Oriented Framework for Event Demutiplexing.http://www.cs.wustl.edu/~doc/pspdfs/Reactor.pdf,2004-12-05
5 Gamma E,Helm R,Johnson R et al.Design Patterns:Elements of Reusable Object-Oriented Software.Massachusetts:Addison-Wesley,1995
6 Schmidt D.C,Huston S D.C++ Network Programming,Volume 1:Mastering Complexity with ACE and Patterns.Massachusetts:Addison-Wesley,2001