第10章 服务配置器(Service Configurator)模式:通过服务配置器模式动态配置通信服务
Prashant Jain Douglas C. Schmidt
一系列迅速增长的通信服务正出现在Internet上。通信服务是为客户提供功能的服务器中的组件。在Internet可用的服务包括:WWW浏览和内容获取服务(例如,Alta Vista、Apache、Netscape的HTTP服务器)、软件发布服务(例如,Castinet)、电子邮件和网络新闻传输代理(例如,sendmail和nntpd)、远程文件访问(例如,ftpd)、远程终端访问(例如,rlogind和telnetd)、路由表管理(例如,gated和routed)、主机和用户活动报告(例如,fingerd和rwhod)、网络时间协议(例如,ntpd),以及请求代理服务(例如,orbixd和RPC portmapper),等等。
实现这些服务的常用方法是将每个服务开发成为单独的程序,随后编译、链接,并在单独的进程中执行每个程序。但是,这样的“静态”配置服务方法生成不灵活、常常也是低效的应用和软件体系结构。静态配置的主要问题是它相对于应用中的其他服务,将特定服务的实现和服务的配置紧耦合在一起。
本文描述服务配置器(Service Configurator)模式,它通过使服务的行为与服务实现被配置进应用的时间点去耦合来增强应用的灵活性(常常还有性能)。本文使用用C++编写的分布式时间服务作为例子来演示服务配置器模式。但是,服务配置器已经以许多方式被实现,范围从现代操作系统(像Solaris和Windows NT)中的设备驱动程序,到Internet超级服务器(像inetd和Windows NT服务控制管理器),以及Java applets。
使服务的行为与服务实现被配置进应用或系统的时间点去耦合。
超级服务器。
服务配置器模式使服务的实现与服务被配置进应用或系统的时间点去耦合。这样的去耦改善了服务的模块性,并允许服务独立于配置问题(比如两个服务是否必须驻留在一起,或是采用何种并发模型执行服务)而持续发展。
此外,服务配置器模式使它配置的服务的管理集中化。这便利了服务的自动初始化和终止,并且可以通过将常用的服务初始化和终止模式分解进高效的可复用组件而提高性能。
这一部分使用分布式时间服务作为例子来说明服务配置器模式的动机。
当服务需要进行动态发起、挂起、恢复和终止时,应该采用服务配置器模式。此外,如果服务配置决策必须被推迟至运行时,也应该采用服务配置器模式。
图10-1 分布式时间服务
为说明该模式的动机,考虑图10-1中所示的分布式时间服务。该服务为在局域网和广域网中协作的计算机提供准确、容错的时钟同步。在要求多个主机维护精确的全局时间的分布式系统中,同步时间服务是很重要的。例如,大型分布式医学成像系统[1]需要全局同步的时钟,以确保病人的检查被精确地记录时间,并由放射科医生通过健康保健递送系统迅捷地加以分析。
如图10-1所示,该分布式时间服务的体系结构含有下列时间服务器(Time Server)、事务员(Clerk)和客户(Client)组件:
一种实现分布式时间服务的方法是将时间服务器、事务员和客户的逻辑功能静态地配置进分离的物理进程。在该方法中,可有一或多个主机运行时间服务器进程;后者处理来自事务员进程的时间更新请求。下面的C++代码段演示静态配置的时间服务器进程的结构:
// The
Clerk_Handler processes time requests
// from Clerks.
class
Clerk_Handler :
public
Svc_Handler <SOCK_Stream>
{
public:
// This method
is called by the Reactor
// when
requests arrive from Clerks.
virtual int
handle_input (void)
{
// Read request from Clerk and
reply
// with the current time.
}
// ...
};
// The
Clerk_Acceptor is a factory that
// accepts connections
from Clerks and creates
//
Clerk_Handlers.
typedef
Acceptor<Clerk_Handler, SOCK_Acceptor> Clerk_Acceptor;
int main (int
argc, char *argv[])
{
// Parse
command-line arguments.
Options::instance
()->parse_args (argc, argv);
// Set up
Acceptor to listen for Clerk connections.
Clerk_Acceptor
acceptor(Options::instance ()->port ());
// Register
with the Reactor Singleton.
Reactor::instance
()->register_handler(&acceptor, ACCEPT_MASK);
// Run the
event loop waiting for Clerks to
// connect and
perform time queries.
for (;;)
Reactor::instance
()->handle_events ();
/* NOTREACHED
*/
}
该程序使用反应堆(Reactor)模式[4]和接受器(Acceptor)模式[5]来实现静态配置的时间服务器进程。
每个需要全局时间同步的主机都可以运行事务员进程。事务员基于接收自一或多个时间服务器的值周期性地更新它们的本地系统时间。下面的C++代码段演示静态配置的事务员进程的结构:
// This class communicates
with the Time_Server.
class
Time_Server_Handler { /* ... */ };
// This class
establishes connections with the
// Time Servers
and periodically queries them for
// their latest
time values.
class Clerk :
public Svc_Handler <SOCK_Stream>
{
public:
// Initialize
the Clerk.
Clerk (void)
{
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);
}
}
// This 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:
typedef
Unbounded_Set <Time_Server_Handler *>HANDLER_SET;
typedef
Unbounded_Set_Iterator<Time_Server_Handler *> ITERATOR;
// Set of
Clerks and iterator over the set.
HANDLER_SET
handler_set_;
// The connector_
is a factory that
// establishes
connections with Time Servers
// and creates
Time_Server_Handlers.
Connector<Time_Server_Handler,
SOCK_Connector>connector_;
};
int main (int
argc, char *argv[])
{
// Parse
command-line arguments.
Options::instance
()->parse_args (argc, argv);
// Initialize
the Clerk.
Clerk clerk;
// Run the
event loop, periodically
// querying the
Time Servers to determine
// the global
time.
for (;;)
Reactor::instance
()->handle_events ();
/* NOTREACHED
*/
}
该程序使用反应堆(Reactor)模式[4]和连接器(Connector)模式[6]来实现静态配置的事务员进程。
客户进程可以使用它们的本地事务员报告的同步的时间。为最少化通信开销,当前时间可被存储在共享内存中,后者被映射到事务员和同一主机上所有客户的地址空间中。除了时间服务,这些主机提供的其他通信服务(比如文件传输、远程登录和HTTP服务器)也可以在分离的静态配置的进程中执行。
尽管像反应堆、接受器和连接器这样的模式的使用改善了上面所示的分布式时间服务器的模块性和可移植性,使用静态方法来配置通信服务有以下缺点:
更为方便和灵活的实现分布式服务的方法常常是使用服务配置器模式。该模式使通信服务的行为与这些服务被配置进应用或系统的时间点去耦合。服务配置器模式消除了以下需求的压力:
图10-2使用OMT表示法来演示根据服务配置器模式设计的分布式时间服务的结构。
图10-2 分布式时间服务的结构
Service基类为配置和控制服务(比如时间服务或事务员)提供标准接口。基于服务配置器的应用使用该接口来发起、挂起、恢复和终止服务,以及获取关于服务的运行时信息(比如它的IP地址和端口号)。服务自身驻留在Service Repository(服务仓库)中,可由基于服务配置器的应用来在Service Repository中增加或移除。
Service基类的两个子类出现在分布式时间服务中:Time
Server和Clerk。每个子类表示一个在分布式时间服务中有着特定功能的具体Service。Time Server服务负责接收和处理来自Clerk的时间更新请求。Clerk服务是连接器(Connector)[6]工厂,负责(1)为每个服务器创建新连接,(2)动态分配新的处理器,以向相连的服务器发送时间更新请求,(3)通过处理器接收来自所有服务器的回复,以及(4)随后更新本地系统时间。
通过管理时间服务中的服务组件的配置,服务配置器模式使分布式时间服务变得更为灵活,并从而使它与实现问题分离开来。此外,服务配置器提供了构架来将其他通信服务的配置和管理合并在一个管理单元中。
当有以下情况时使用服务配置器模式:
当有以下情况时不要使用服务配置器模式:
在图10-3中使用OMT表示法演示了服务配置器模式的结构:
图10-3 服务配置器模式的结构
服务配置器模式中的关键参与者包括:
图10-4描述下面的服务配置器模式三个阶段中的组件间协作:
图10-4 服务配置器模式的交互图
服务配置器模式提供以下好处:
服务配置器模式有以下缺点:
服务配置器模式可以通过许多方式实现。这一部分解释实现该模式时所涉及的步骤和可选方案。表10-1中总结了这些步骤和可选方案。
步骤 |
常用可选方案 |
定义服务控制接口 |
|
定义服务仓库 |
|
选择配置机制 |
|
确定服务执行机制 |
|
表10-1 实现服务配置器模式所涉及的步骤
定义服务控制接口有两种基本的方法,基于继承的和基于消息的:
class Service
{
public:
// =
Initialization and termination hooks.
virtual int init
(int argc, char *argv[]) = 0;
virtual int
fini (void) = 0;
// = Scheduling
hooks.
virtual int
suspend (void);
virtual int
resume (void);
// =
Informational hook.
virtual int
info (char **, size_t) = 0;
};
init方法用作Service的入口。它被服务配置器用于初始化Service的执行。fini方法允许服务配置器终止Service的执行。suspend和resume方法是调度挂钩,由服务配置器用于挂起和恢复Service的执行。info方法允许服务配置器获取与Service相关的信息(比如它的名字和网络地址)。这些方法一起给出了服务配置器和由它管理的Service之间的统一约定。
Windows NT服务控制管理器(SCM)使用这种方案。每个Windows NT主机都拥有主SCM进程,通过向系统服务传递多种控制信息(比如PAUSE、RESUME和TERMINATE)来自动对其进行初始化和管理。每个SCM管理的服务的开发者都要负责编写代码来处理这些消息。
下面的代码介绍一个用C++写成的服务配置器模式的例子。该例子聚焦于10.2.3中介绍的分布式时间服务的与配置有关的方面。此外,它还演示了其他模式(比如反应堆模式[4]、接受器[5]和连接器[6]模式)的使用,它们通常被用于开发通信服务和对象请求代理(Object Request Broker)。
在下面的例子中,图10-3所示的OMT类图中的Concrete Service类由Time Server类以及Clerk类来表示。这一部分中的C++代码实现Time Server和Clerk类。两个类都继承自Service,使得它们可被动态配置进应用中。此外,该方法使用基于显式动态链接[7] 和配置文件的配置机制来动态地配置分布式时间服务的事务员和服务器部分。服务执行机制则基于在单线程控制中的反应式事件处理模型。
下面的例子显示事务员组件可以怎样改变它用于计算本地系统时间的算法、而又不影响服务配置器配置的其他组件的执行。一旦算法被修改,事务员组件就由服务配置器进行动态重配置。
下面所示的代码还包括main驱动函数,它提供任何基于服务配置器的应用的通用入口。使用ACE[7],该实现可运行在UNIX/POSIX和Win32平台上;ACE可通过WWW在http://www.cs.wustl.edu/~schmidt/ACE.html获取。
Time Server使用Acceptor类来接受来自一或多个事务员的连接。Acceptor类使用接受器模式[5]来为所有来自事务员的连接创建处理器;这些事务员想要接收时间更新请求。该设计使Time Server的实现与它的配置去耦合,因此,开发者可以独立于Time Server的配置改变它的实现。这为改进时间服务器的实现提供了灵活性。
Time Server类继承自在10.7中定义的Service基类。这使得服务配置器能够动态地链接Timer
Server,以及解除其链接。在将Time Server服务装载进Service Repository之前,服务配置器调用它的init挂钩。该方法执行Time Server特有的初始化代码。同样地,当服务不再被需要时,fini挂钩方法被服务配置器自动调用,以将其终止。
// The
Clerk_Handler processes time requests
// from Clerks.
class
Clerk_Handler :
public
Svc_Handler <SOCK_Stream>
{
// This is
identical to the Clerk_Handler
// defined in
the second section.
};
class
Time_Server : public Service
{
public:
// Initialize
the service when linked dynamically.
virtual int
init (int argc, char *argv[])
{
// Parse command line arguments to
get
// port number to listen on.
parse_args (argc, argv);
// Set the connection acceptor
endpoint into
// listen mode (using the Acceptor
pattern).
acceptor_.open (port_);
// Register with the Reactor
Singleton.
Reactor::instance
()->register_handler
(&acceptor_, ACCEPT_MASK);
}
// Terminate
the service when dynamically unlinked.
virtual int
fini (void)
{
// Close down the connection.
acceptor_.close ();
}
// Other
methods (e.g., info(), suspend(), and
// resume())
omitted.
private:
// Parse
command line arguments or those
// specified by
the configuration file.
int parse_args
(int argc, char *argv[]);
// Acceptor is
a factory that accepts
// connections
from Clerks and creates
//
Clerk_Handlers.
Acceptor<Clerk_Handler,
SOCK_Acceptor>acceptor_;
// Port the
Time Server listens on.
int port_;
};
注意服务配置器也可以分别通过调用Time
Server的suspend和resume挂钩来将其挂起和恢复。
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 <SOCK_Stream>
{
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 <Time_Server_Handler *>HANDLER_SET;
typedef
Unbounded_Set_Iterator<Time_Server_Handler *> ITERATOR;
// Set of
Clerks and iterator over the set.
HANDLER_SET
handler_set_;
// Connector
used to set up connections
// to all
servers.
Connector<Time_Server_Handler,
SOCK_Connector>connector_;
};
通过遍历其处理器列表,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]的实现。下面对它们作一概述:
要回应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:
服务配置器模式的意图与配置(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.