许多分布式系统都含有一组全局服务。应用开发者可以调用这些服务来帮助他满足分布式开发的需求。在构造分布式应用时,需要像名字服务、远程终端访问服务、登录和时间时间服务这样的全局服务。构造这些服务的一种办法是将每个服务编写成单独的程序。随后这些服务程序就在它们自己的私有进程中执行和运行。但是,这样的方法会导致配置的噩梦。管理员需要管理每一个节点,依据当前的用户需求和策略来执行服务程序。如果需要增加新服务,或是需要移除旧服务,管理员就必须将时间花费在每台机器上、重新进行配置。此外,这样的配置是静态的。要进行重配置,管理员需要人工地终止服务(通过杀掉服务进程),随后重启一个替换服务。同样,在机器上运行的服务也可能不被任何应用所使用。显然,这样的方法是低效的和不合需要的。
如果服务可以被动态地启动、移除、挂起和恢复,那将会方便得多。这样,服务开发者就不必再担心配置的服务。他所需关心的是服务如何完成工作。管理员就可以在应用中增加或替换新服务,而不用重新编译或关闭服务进程。
服务配置器模式可以完成所有这些任务。它使服务的实现与配置去耦合。无需关闭服务器,就可以在应用中增加新服务和移除旧服务。在大多数情况下,提供服务的服务器都被实现为看守(
daemon)进程。构架组件

图
8-1 ACE服务配置器中的组件及其关系
ACE
中的服务配置器由以下组件组成:
ACE_Service_Object
包括了一些由框架调用的方法,用于服务要启动(init())、停止(fini())、挂起(suspend())或是恢复(resume())时。ACE_Service_Object派生自ACE_Shared_Object和ACE_Event_Handler。ACE_Shared_Object在应用想要使用操作系统的动态链接机制来进行加载时被用作抽象基类。ACE_Event_Handler已在对反应堆的讨论中进行了介绍。当开发者想要他的类响应来自反应堆的事件时,他就从ACE_Event_Handler派生他的子类。为什么服务对象要从
ACE_Event_Handler继承?用户发起重配置的一种方法是生成一个信号;当这样的信号事件发生时,反应堆被用于处理信号,并向ACE_Service_Config发出重配置请求。除此而外,软件的重配置也可能在某事件产生后发生。因而所有的服务对象都被构造为能对事件进行处理。服务配置文件有它自己的简单脚本,用于描述你想要服务怎样启动和运行。你可以定义你是想要增加新服务,还是挂起、恢复或移除应用中现有的服务。另外还可以给服务发送参数。服务配置器还允许进行基于
ACE的流(stream)的重配置。我们将在讨论了ACE流构架之后再来更多地讨论这一点。定义配置文件
服务配置文件指定在应用中哪些服务要被加载和启动。此外,你可以指定哪些服务要被停止、挂起或恢复。还可以发送参数给你的服务对象的
init()方法。
8.2.1
启动服务
服务可以被静态或动态地启动。如果服务要动态启动,服务配置器实际上会从共享对象库(也就是,动态链接库)中加载服务对象。为此,服务配置器需要知道哪个库含有此对象,并且还需要知道对象在该库中的名字。因而,在你的代码文件中你必须通过你需要记住的名字来实例化服务对象。于是动态服务会这样被配置:
dynamic service_name type_of_service * location_of_shared_lib:name_of_object “parameters”
而静态服务这样被初始化:
static service_name “parameters_send_to_service_object”
8.2.2
挂起或恢复服务如刚才所提到的,你在启动服务时分配给它一个名字。这个名字随后被用于挂起或恢复该服务。于是要挂起服务,你所要做的就是在
svc.conf文件中指定:
suspend service_name
这使得服务对象中的
suspend()方法被调用。随后你的服务对象就应该挂起它自己(基于特定服务不同的“挂起”含义)。如果你想要恢复这个服务,你所要做的就是在
svc.conf文件中指定:
resume service_name
这使得服务对象中的
resume()方法被调用。随后你的服务对象就应该恢复它自己(基于特定服务不同的“恢复”含义。)
8.2.3
停止服务
停止并移除服务(如果服务是动态加载的)同样是很简单的操作,可以通过在你的配置文件中指定以下指令来完成:
remove service_name
这使得服务配置器调用你的应用的
fini()方法。该方法应该使此服务停止。服务配置器自己会负责将动态对象从服务器的地址空间里解除链接。编写服务
为服务配置器编写你自己的服务相对比较简单。你可以让这个服务做任何你想做的事情。唯一的约束是它应该是
ACE_Service_Object的子类。所以它必须实现init()和fini()方法。在ACE_Service_Config被打开(open())时,它读取配置文件(也就是svc.conf)并根据这个文件来对服务进行初始化。一旦服务被加载,它会调用该服务对象的init()方法。类似地,如果配置文件要求移除服务,fini()方法就会被调用。这些方法负责分配和销毁服务所需的任何资源,比如内存、连接、线程等等。在svc.conf文件中指定的参数通过服务对象的init()方法来传入。下面的例子演示一个派生自
ACE_Task_Base的服务。ACE_Task_Base类含有activate()方法,用于在对象里创建线程。(在“任务和主动对象”一章中讨论过的ACE_Task派生自ACE_Task_Base,并包括了用于通信目的的消息队列。因为我们不需要我们的服务与其它任务通信,我们仅仅使用ACE_Task_Base来帮助我们完成工作。)更多详细信息,请阅读“任务和主动对象”一章。该服务是一个“无为”(do-nothing)的服务,一旦启动,它只是周期性地广播当天的时间。
