ACE技术论文集-第10章 通过服务配置器模式动态配置通信服务
发布: 2008-6-13 14:34 | 作者: Prashant Jain and D | 来源: 转载 | 查看: 171次
Clerk使用Connector类来建立和维护与一或多个Time Server的连接。Connector类使用连接器模式[6]来为每个到时间服务器的连接创建处理器。处理器接收并处理来自Time Server的时间更新。
Clerk类继承自Service基类。因此,像Time Service一样,它也可以被服务配置器动态地配置。服务配置器可以分别通过调用Clerk的init、suspend、resume和fini挂钩来将其初始化、挂起、恢复和终止。
// This class communicates with the Time_Server.
class Time_Server_Handler
: public Svc_Handler
{
public:
// Get the current time from a Time Server.
Time_Value get_server_time (void);
// ...
};
// This class establishes and maintains connections
// with the Time Servers and also periodically queries
// them to calculate the current time.
class Clerk : public Service
{
public:
// Initialize the service when linked dynamically.
virtual int init (int argc, char *argv[])
{
// Parse command line arguments and for
// every host:port specification of server,
// create a Clerk instance that handles
// connection to the server.
parse_args (argc, argv);
Time_Server_Handler **handler = 0;
// Use the Iterator pattern and the
// Connector pattern to set up the
// connections to the Time Servers.
for (ITERATOR iterator (handler_set_);
iterator.next (handler) != 0;
iterator.advance ())
{
connector_.connect (*handler);
Time_Value timeout_interval (60);
// Register a timer that will expire
// every 60 seconds. This will trigger
// a call to the handle_timeout() method,
// which will query the Time Servers and
// retrieve the current time of day.
Reactor::instance ()->schedule_timer
(this, timeout_interval);
}
}
// Terminate the service when dynamically unlinked.
virtual int fini (void)
{
Time_Server_Handler **handler = 0;
// Disconnect from all the time servers.
for (ITERATOR iterator (handler_set_);
iterator.next (handler) != 0;
iterator.advance ())
(*handler)->close ();
// Remove the timer.
Reactor::instance ()->cancel_timer (this);
}
// info(), suspend(), and resume() methods omitted.
// The handle_timeout method implements the
// Clock Synchronization algorithm that computes
// local system time. It is called periodically
// by the Reactor’s timer mechanism.
int handle_timeout (void)
{
// Periodically query the servers by iterating
// over the handler set and obtaining time
// updates from each Time Server.
Time_Server_Handler **handler = 0;
// Use the Iterator pattern to query all
// the Time Servers
for (ITERATOR iterator (handler_set_);
iterator.next (handler) != 0;
iterator.advance ())
{
Time_Value server_time = (*handler)->get_server_time ();
// Compute the local system time and
// store this in shared memory that
// is accessible to the Client processes.
}
}
private:
// Parse command line arguments or those
// specified by the configuration file and
// create Clerks for every specified server.
int parse_args (int argc, char *argv[]);
typedef Unbounded_Set HANDLER_SET;
typedef Unbounded_Set_Iterator ITERATOR;
// Set of Clerks and iterator over the set.
HANDLER_SET handler_set_;
// Connector used to set up connections
// to all servers.
Connectorconnector_;
};
通过遍历其处理器列表,Clerk周期性地发送时间更新请求给所有与其相连的Time Server。一旦Clerk接收到来自所有与其相连的Time Server的响应,它就重新计算它的本地系统时间。因而,当客户向事务员请求当前时间时,它们会接收到全局同步的时间值。
下面的代码演示应用的动态配置和执行,使用配置文件来使Time Server和Clerk共同驻留在同一OS进程中:
int main (int argc, char *argv[])
{
// Configure the daemon.
Service_Config daemon (argc, argv);
// Perform daemon services updates.
daemon.run_event_loop ();
/* NOTREACHED */
}
这个完全通用的main程序在Service Config对象的构造器中动态地配置通信服务。该方法查询下面的svc.conf配置文件:
# Configure a Time Server.
dynamic Time_Server Service*
netsvcs.dll:make_Time_Server()
"-p $TIME_SERVER_PORT"
# Configure a Clerk.
dynamic Clerk Service*
netsvcs.dll:make_Clerk()
"-h tango.cs:$TIME_SERVER_PORT"
"-h perdita.wuerl:$TIME_SERVER_PORT"
"-h atomic-clock.lanl.gov:$TIME_SERVER_PORT"
"-P 10" # polling frequency
ACE服务配置构架处理svc.conf配置文件中的每一条目。该构架将dynamic指令解释为将指定Service动态链接进应用进程的命令。表10-2总结了可用的服务配置指令。
|
指令 |
描述 |
|
dynamic |
动态链接和启用服务 |
|
static |
启用静态链接的服务 |
|
remove |
完全地移除服务 |
|
suspend |
挂起服务,而不将其移除 |
|
resume |
恢复先前挂起的服务 |
表10-2 服务配置指令

图10-5 重配置时间服务器和事务员
例如,svc.conf文件中的第一个条目指定一个服务名(Time_Server),由Service Repository用于标识动态配置的服务。make_Time_Server是一个工厂函数,位于动态链接库netsvcs.dll中。服务配置器构架动态地将此DLL链接进应用的地址空间,然后调用make_Time_Server工厂函数。该函数动态地分配新的Time Server实例,如下所示:
Service *make_Time_Server(void)
{
return new Time_Server;
}
在第一个条目中最后的字符串参数指定含有端口号的环境变量,Time Server将在该端口上侦听Clerk连接。服务配置器将此字符串转换为“argc/argv”风格的向量,并将其传递给Time Server的init挂钩。如果init方法成功执行,Service * 就被存储在Service Repository中的Time_Server名下。
svc.conf文件中的第二个条目指定怎样动态配置Clerk。和前面一样,服务配置器将netsvcs.dll动态地链接进应用的地址空间,并调用make_Clerk工厂函数来创建新的Clerk实例。三个时间服务器(tanggo.cs、perdita.wuerl和atomic-clock.lanl)的名字和端口号被传递给init挂钩。此外,-P 10选项定义事务员轮询时间服务器的频度。
假设我们不想将Time Server和Clerk放在一起,以减少应用的内存占用。因为我们正在使用服务配置器模式,所有要做的就是将svc.conf文件划分为两部分。一部分含有Time Server条目,而另一部分含有Clerk条目。因为服务配置器模式有将服务的行为与配置去耦合这一优点,服务自身可以无需改变。图10-5显示在Time Server和Clerk驻留在同一进程时、以及在划分后配置看起来是怎样的。
现在假设我们需要改变Clerk的算法实现。例如,我们可能决定从Berkeley算法[2]的实现切换到Cristian的算法[3]的实现。下面对它们作一概述:
- Berkeley算法:在该方法中,Time Server是主动的组件,周期性地轮询网络中的全部机器,询问那里的时间。基于接收到的响应,它计算正确时间的合计值,并告诉所有机器相应地调整它们的时间。
- Cristian算法:在该方法中,Time Server是响应事务员作出的查询的被动实体。它不会主动地查询其他机器来确定它自己的时间。
要回应Time Server的特性,可能必需改变时间同步算法。例如,如果Time Server所驻留的机器上有WWV接收器,Time Server可被用作被动的实体,而Cristian算法也可能就是适合的。在另一方面,如果Time Server所驻留的机器上没有WWV接收器,那么Berkeley算法的实现可能就更为适合。
图10-5显示事务员实现中的改变(相应于时钟同步算法中的改变)。改变发生在分离Time Server和Clerk的过程中,它们先前是驻留在一起的。
理想地,我们想要改变算法实现,而又不影响其他服务或时间服务的其他组件的执行。使用服务配置器来实现此要求只需要简单地对svc.conf文件作出下面的修改:
# Terminate Clerk
remove Clerk
仅有的额外要求是让服务配置器处理该指令。这可以通过生成外部事件(比如UNIX SIGHUP信号、RPC通知,或是Windows NT Registry事件)来完成。收到该事件,应用将再次查询配置文件,并终止Clerk服务的执行。服务配置器将调用Clerk的fini方法,并从而终止事务员组件的执行。其他服务的执行不会受影响。
一旦事务员服务被终止,就可以对算法实现进行改变。随后代码就可以被重编译和重链接,以形成新的netsvcs DLL。也可以采取类似的方法来将事务员服务增加回服务配置器。可以修改配置文件,用新指令指定事务员需被动态链接,如下所示:
# Reconfigure a new Clerk.
dynamic Clerk Service*
netsvcs.dll:make_Clerk()
"-h tango.cs:$TIME_SERVER_PORT"
"-h perdita.wuerl:$TIME_SERVER_PORT"
"-h atomic-clock.lanl.gov:$TIME_SERVER_PORT"
随后生成一个外部事件,致使进程重新读入配置文件,并将事务员组件增加到仓库中。一旦Clerk的init方法被服务配置器构架调用,事务员组件就将开始执行。
图10-6显示像事务员服务这样的Service的生存期状态图。

图10-6 服务的生命周期状态图
注意在终止和移除事务员服务的整个过程中,没有其他活动服务受到实现改变和事务员服务重配置的影响。替换新的服务实现的容易程度进一步例证了服务配置器模式所提供的灵活性。
服务配置器模式被广泛用于系统和应用编程环境中,包括UNIX、Windows NT、ACE和Java applets:
- 现代操作系统设备驱动程序:大多数现代操作系统(比如Solaris和Windows NT)支持动态可配置的内核级设备驱动程序。这些驱动程序可以经由init/fini/info挂钩动态地链接进系统,或从系统中解除链接。这些操作系统使用服务配置器模式来使管理员能够重配置OS内核,而又不必将其关闭、重编译和静态地重新链接新驱动程序,以及重启系统。
- UNIX网络看守管理:服务配置模式已被用于管理UNIX网络看守的“超级服务器”(Superserver)中。两个广泛可用的网络看守管理构架是inetd[10]和listen[11]。两个构架都查询配置文件,后者指定(1)服务名(比如标准Internet服务ftp、telnet、daytime和echo),(2)端口号,用于在其上侦听连接到这些服务的客户,以及(3)可执行文件,在客户连接时调用并执行服务。两个构架都含有主接受器[5]进程,它监控一组与这些服务相关联的端口。当客户连接在被监控端口上发生时,接受器进程接受该连接,并将请求多路分离给适当的预登记的服务处理器。该处理器执行服务(反应式地或在主动对象中),并将任何结果返回给客户。
- Windows NT服务控制管理器(SCM):不像inetd和listen,Windows NT服务控制管理器(SCM)本身不是端口监控器。就是说,它不提供内建支持来侦听一组I/O端口、并在客户请求到达时“按需”分派服务器进程。相反,它提供基于RPC的接口,允许主SCM进程自动发起和控制(也就是,暂停、恢复、终止,等等)管理员安装的服务(比如远程注册表访问)。这些服务将另外作为单服务或多服务看守进程中的分离线程运行。每个被安装的服务都单独负责配置它自己,监控任意通信端点(可以比socket端口更广泛)。例如,SCM可以控制命名管道和共享内存。
- ACE自适配通信环境构架:ACE构架[7]为动态配置和控制通信服务提供了一组C++机制。ACE Service Configurator扩展了inetd、listen和SCM所提供的机制,以自动支持通信服务的动态链接和解除链接。10.8中包含的代码显示了怎样将ACE构架用于实现分布式时间服务。ACE所提供的机制受到了现代操作系统中用于配置和控制设备驱动程序的接口的影响。但是, ACE Service Configurator构架聚焦于应用级Service对象的动态配置和控制,而非针对内核级设备驱动程序。
- Java applets:Java中的applet机制使用了服务配置器模式。Java支持下载、初始化、启动、挂起、恢复和终止applet。它通过提供发起和终止线程的方法(例如,start和stop)来提供这样的支持。Java applet中的方法可以使用Thread.currentThread()来访问它在其中运行的线程,并随后向它发出控制消息,比如suspend、resume和stop。[12]介绍了一个例子,演示服务配置器模式是怎样被用于Java applets的。
服务配置器模式的意图与配置(Configuration)模式 [13]相类似。配置模式使分布式应用中相关于服务配置的、结构上的问题与服务自身的执行去耦合。它被用于在一些构架中配置分布式系统,以支持从一组组件构造一个分布式系统。以类似的方式,服务配置器模式使服务初始化与服务处理去耦合。主要的区别是配置模式更多地聚焦于一系列相关服务的主动合成,而服务配置器模式聚焦于在特定端点上的服务处理器的动态初始化。此外,服务配置器模式还聚焦于使服务行为与服务的并发策略去耦合。
管理器模式(Manager Pattern)[14]通过承担创建和删除一组对象的责任来对其进行管理。此外,它还提供一个接口,以允许客户访问它管理的对象。服务配置器模式可以使用管理器模式来按照需要创建和删除服务,以及维护它使用管理器模式创建的服务的仓库。但是,必须为使用管理器模式创建的服务增加动态配置、初始化、挂起、恢复和终止功能,才能全面地实现服务配置器模式。
服务配置器常常利用反应堆[4]模式来为被配置的服务完成事件多路分离和分派。同样地,长执行周期的、动态配置的服务常常使用主动对象模式[15]。
基于服务配置器的系统的管理接口(比如配置文件或GUI)提供了一个外观(Fa?ade)[16]。该外观简化了在服务配置器中执行的应用的管理和控制。
Service基类提供的虚方法是一些回调“挂钩”[9]。这些挂钩被服务配置器用于发起、挂起、恢复和终止服务。
可以使用工厂方法(Factory Method)[16]来创建Service。这允许应用决定创建何种类型的Service。
本文描述服务配置器模式,并阐释了它怎样使服务的实现与它们的配置去耦合。这样的去耦合增强了服务的灵活性和可扩展性。特别地,服务实现可以独立于许多相关于服务配置的问题而持续开发和改进。此外,服务配置器还提供了重配置服务、而又无需修改、重编译或静态重链接已有代码的功能。
服务配置器还使其配置的服务的管理集中化。通过使通用的服务初始化任务自动化(比如打开和关闭文件、获取和释放锁,等等),这样的集中化可以简化编程工作。此外,集中式管理还对服务的生存期提供了更大的控制。
服务配置器模式已被广泛地用于许多环境中。本文使用了一个用C++编写的分布式时间服务作为例子来演示服务配置器模式。使分布式时间服务的组件的开发与它们被配置进系统的时间点去耦合的能力例证了服务配置器模式所提供的灵活性。这样的去耦合允许开发者采用不同的分布式时间算法开发不同的事务员。配置特定事务员的决策成为运行时决策,从而带来了更大的灵活性。本文还显示了怎样将服务配置器模式用于动态重配置分布式时间服务,而又无须修改、重编译或静态重链接运行中的服务器。
服务配置器模式被广泛用于许多环境中,比如Solaris和Windows NT的设备驱动程序、像inetd这样的Internet超级服务器,Windows NT服务控制管理器,以及ACE构架。在每种情况下,服务配置器都使服务的实现与服务的配置得以去耦合。这样的去耦合对应用的可扩展性和灵活性都提供了支持。
[1] I. Pyarali, T. H. Harrison, and D. C. Schmidt, “Design and Performance of an Object-Oriented Framework for High-Performance Electronic Medical Imaging,” USENIX Computing Systems, vol. 9, November/December 1996.
[2] R. Gusella and S. Zatti, “The Accuracy of the Clock Synchronization Achieved by TEMPO in Berkeley UNIX 4.3BSD,” IEEE Transactionson Software Engineering, vol. 15, pp. 847–853, July 1989.
[3] F. Cristian, “Probabilistic Clock Synchronization,” Distributed Computing, vol. 3, pp. 146–158, 1989.
[4] D. C. Schmidt, “Reactor: An Object Behavioral Pattern for Concurrent Event Demultiplexing and Event Handler Dispatching,” in Pattern Languages of Program Design (J. O. Coplien and D. C. Schmidt, eds.), Reading, MA: Addison-Wesley, 1995.
[5] D. C. Schmidt, “Design Patterns for Initializing Network Services: Introducing the Acceptor and Connector Patterns,” C++ Report, vol. 7, November/December 1995.
[6] D. C. Schmidt, “Connector: a Design Pattern for Actively Initializing Network Services,” C++ Report, vol. 8, January 1996.
[7] D. C. Schmidt and T. Suda, “An Object-Oriented Framework for Dynamically Configuring Extensible Distributed Communication Systems,” IEE/BCS Distributed Systems Engineering Journal (Special Issue on Configurable Distributed Systems), vol. 2, pp. 280–293, December 1994.
[8] R. G. Lavender and D. C. Schmidt, “Active Object: an Object Behavioral Pattern for Concurrent Programming,” in Pattern Languages of Program Design (J. O. Coplien, J. Vlissides, and N. Kerth, eds.), Reading, MA: Addison-Wesley, 1996.
[9] W. Pree, Design Patterns for Object-Oriented Software Development. Reading, MA: Addison-Wesley, 1994.
[10] W. R. Stevens, UNIX Network Programming, First Edition. Englewood Cliffs, NJ: Prentice Hall, 1990.
[11] S. Rago, UNIX System V Network Programming. Reading, MA: Addison-Wesley, 1993.
[12] P. Jain and D. C. Schmidt, “Service Configurator: A Pattern for Dynamic Configuration of Services,” in Proceedings of the 3rd Conference on Object-Oriented Technologies and Systems, USENIX, June 1997.
[13] S. Crane, J. Magee, and N. Pryce, “Design Patterns for Binding in Distributed Systems,” in The OOPSLA ’95 Workshop on Design Patterns for Concurrent, Parallel, and Distributed Object-Oriented Systems, (Austin, TX), ACM, Oct. 1995.
[14] P. Sommerland and F. Buschmann, “The Manager Design Pattern,” in Proceedings of the 3rd Pattern Languages of Programming Conference, September 1996.
[15] R. G. Lavender and D. C. Schmidt, “Active Object: an Object Behavioral Pattern for Concurrent Programming,” in Proceedings of the 2nd Annual Conference on the Pattern Languages of Programs, (Monticello, Illinois), pp. 1–7, September 1995.
[16] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.
