The ACE_Service_Config Class

类别:编程语言 点击:0 评论:0 推荐:
Ru-Brd
5.4 The ACE_Service_Config Class Motivation

Before a service can execute, it must be configured into an application's address space. One way to configure services into a networked application is to statically link the functionality provided by its various classes and functions into separate OS processes, and then manually instantiate or initialize them at run time. We used this approach in the logging server examples in Chapters 3 and 4 and throughout C++NPv1, where the logging server program runs in a process that handles log records from client applications. Although our use of the ACE Reactor framework in earlier chapters improved the networked logging server's modularity and portability, the following drawbacks arose from statically configuring the Reactor_Logging_Server class with its main() program:


在服务可以执行前,它必须配置进应用程序。一种方法是静态的连接由不同的类和函数提供的功能到独立的OS进程中,然后在运行时手工实例化他们。我们在C++NPV1的第3,第4章中使用这种方法,logging server运行在一个处理客户日志的进程中。虽然我们使用了ACE Reactor框架提高了模块化,可移植性,但是在main()中静态配置Reactor_Logging_Server出现了类下面的问题:

Service configuration decisions are made prematurely in the development cycle, which is undesirable if developers don't know the best way to collocate or distribute services in advance. Moreover, the "best" configuration may change as the computing context changes. For example, an application may write log records to a local file when it's running on a disconnected laptop computer. When the laptop is connected to a LAN, however, it may forward log records to a centralized logging server. Forcing networked applications to commit prematurely to a particular service configuration impedes their flexibility and can reduce their performance and functionality. It can also force costly redesign and reimplementation later in a project's life cycle.


开发周期中过早的做出服务配置的决定,开发人员不知道最佳的配置或分配服务。而且最佳的配置会随着计算机环境的变化而变化。应用程序可以将长纪录写到一个本地文件当网络断开时,也可以将记录转发给日志服务器当网络连接上时。过早的提交配置阻碍了灵活性减少了功能和效率。加强了重新实现的昂贵开销。

Modifying a service may affect other services adversely if the implementation of a service is coupled tightly with its initial configuration. To enhance reuse, for example, a logging server may initially reside in the same program as other services, such as a name service. If the other services change, however, for example if the name service lookup algorithm changes, all existing code in the server would require modification, recompilation, and static relinking. Moreover, terminating a running process to change some of its service code would also terminate the collocated logging service. This disruption in service may not be acceptable for highly available systems, such as telecommunication switches or customer care call centers [SS94].


如果一个服务和他初始配置有紧密联系,修改一个服务可能影响其他服务adversely。为了增强重用,举例,一个logging server可能和其他服务,如名字服务,驻留在同一个程序中。如果其他服务变化了,如名字服务修改了查找算法,服务中所有的代码需要重新修改,编辑,静态连接。而且终止一个运行的进程来改变服务中的代码也结束了日志服务。这个中断在一些高可靠系统中是不可接受的,如telecommunication switches和customer care call centers。

System performance may scale poorly since associating a separate process with each service ties up OS resources, such as I/O handles, virtual memory, and process table slots. This design is particularly wasteful if services are often idle. Moreover, processes can be inefficient for many short-lived communication tasks, such as asking a time service for the current time or resolving a host address request via the Domain Name Service (DNS).


系统性能的扩展小因为相互关联的进程和它的每一个服务与系统资源紧密联系,如I/O HANDLE,虚拟内存,和进程槽。这种设计是非常浪费的如果服务经常处理空闲。而且进程可能低效率的服务许多short-lived communication tasks,例如询问time service获得当前时间,或者解析一个主机地址通过DNS。

To address the drawbacks of purely static configurations, the ACE Service Configurator framework defines the ACE_Service_Config class.

Class Capabilities

ACE_Service_Config implements the Facade pattern [GoF] to integrate other classes in the ACE Service Configurator framework and coordinate the activities necessary to manage the services in an application. This class provides the following capabilities:

ACE_Service_Config 实现了Facade pattern [GoF] 与其他ACE Service Configurator framework中的类结合在一起,调整必需的行为来管理程序中其他的服务。这个类提供了以下的能力:

It interprets a scripting language that allows applications or administrators to provide the ACE Service Configurator framework with commands, called directives, to locate and initialize a service's implementation at run time, as well as to suspend, resume, reinitialize, and/or shut down a component after it's been initialized. Directives can be specified to ACE_Service_Config in either of two ways:

他解释了脚本语言允许程序或者管理员在运行时提供ACE Service Configurator framework命令,调用指令,来定位和初始化服务的实现,例如挂起,恢复,重新初始化,shut down一个部件在他初始化后。指令可以用两种方法指定:

Using configuration files (named svc.conf by default) that contain one or more directives

使用包含一条或者多条指定的配置文件,默认是svc.conf

Programmatically, by passing individual directives as strings

Programmatically,通过字符串方式传递单独指令

It supports the management of services located in the application (the so-called static services), as well as those that must be linked dynamically (the so-called dynamic services) from separate shared libraries (DLLs).

他支持服务管理。

It allows service reconfiguration at run time using the following mechanisms:

On POSIX platforms, ACE_Service_Config can be integrated with the ACE Reactor framework to reprocess its configuration files upon receipt of a SIGHUP signal or any other user-specified signal, such as SIGINT.

By passing the "reconfigure" command via ACE_Service_Manager, as described in Sidebar 31 (page 132).

An application can request its ACE_Service_Config to reprocess its configuration files at any time. For example, a Windows directory change notification event can be used to help a program learn when its configuration file changes. This change event can then trigger reprocessing of the configuration.

An application can also specify individual directives for its ACE_Service_Config to process at any time via the process_directive() method.

程序也可以在任何时候通过process_directive方法对ACE_Service_Config指定单独的指令在任何时候。 Figure 5.6 The ACE_Service_Config Class

ACE_Service_Config + ACE_Service_Config (ignore_static_svcs : int = 1, repository_size : size_t = MAX_SERVICES, signum : int = SIGHUP) + open (argc : int, argv : ACE_TCHAR *[], logger_key : const ACE_TCHAR * = ACE_DEFAULT_LOGGER_KEY, ignore_static_svcs : int = 1, ignore_default_svc_conf : int = 0, ignore_debug_flag : int = 0) : int + close () : int + process_directives () : int + process_directive (directive : ACE_TCHAR[]) : int + reconfigure () : int + suspend (name : const ACE_TCHAR []) : int + resume (name : const ACE_TCHAR []) : int

The interface for ACE_Service_Config is shown in Figure 5.6. This class has a rich interface since it exports all the features in the ACE Service Configurator framework. We therefore group the description of its methods into the three categories described below.

1. Service Configurator life cycle management methods. The following methods initialize and shut down the ACE_Service_Config:

生命周期管理方法

ACE Class

Description

ACE_Service_Config() open()

These methods create and initialize the ACE_Service_Config.

close()

This method shuts down and finalizes all the configured services and deletes the resources allocated when the ACE_Service_Config was initialized.

There's only one instance of ACE_Service_Config's state in a process. This class is a variant of the Monostate pattern [CB97], which ensures a unique state for its instances by declaring all data members to be static. Moreover, the ACE_Service_Config methods are also declared as static. The ACE_Service_Config constructor, however, is the only way to set the maximum size of the ACE_Service_Repository. It's also the only programmatic way to change the signal number that can be registered with the reactor to trigger reconfigurations. Instantiating an instance of ACE_Service_Config therefore simply sets these parameters for the underlying monostate object and does not create a separate configuration object. Thus, the ACE_Service_Config destructor is a no-op.

ACE_Service_Config的状态只有一个实例在整个进程中。 This class is a variant of the Monostate pattern [CB97], 通过将所有成员变量声明为静态来保证实例的唯一状态。而且ACE_Service_Config方法也是静态的。ACE_Service的构造函数是唯一的方法来设置ACE_Service_Repository的大小。他也可以通过编程的方法改变signal number that can be registered with reactor来触发重配置。简单的设置monostate object的参数来安装一个ACE_Service_Config实例,不要创建单独configuration object,ACE_Service_Config奚构函数没有操作。

The open() method is the common way of initializing the ACE_Service_Config. It parses arguments passed in the argc and argv parameters, skipping the first parameter (argv[0]) since that's the name of the program. The options recognized by ACE_Service_Config are outlined in the following table:

open是一个通常初始化ACE_Service_Config的方法。他解析通过argc和argv传入的参数,跳过argv[0]因为那是程序的名字。以下是ACE_Service_Config是别的选项:

Option

Description

'-b'

Turn the application process into a daemon (see Sidebar 5 on page 32).转变进成为守护进程。

'-d'

Display diagnostic information as directives are processed.显示指令处理时诊断信息

'-f'

Supply a file containing directives other than the default svc.conf file. This argument can be repeated to supply multiple configuration files.提供一个配置文件。

'-n'

Don't process static directives, which eliminates the need to initialize the ACE_Service_Repository statically.

'-s'

Designate the signal to be used to cause the ACE_Service_Config to reprocess its configuration file. By default, SIGHUP is used.制定一个信号用来重新处理配置文件。

'-S'

Supply a directive to the ACE_Service_Config directly. This argument can be repeated to process multiple directives.直接提供一个指令给ACE_Service_Config。

'-y'

Process static directives, which requires the static initialization of the ACE_Service_Repository.静态连接

2. Service configuration methods. After parsing all its argc/argv arguments, the ACE_Service_Config::open() method calls one or both of the following methods to configure the application:

服务配置方法。open通过调用以下方法来配置程序。

Method

Description

process_directives()

Process a sequence of directives that are stored in the designated script file(s). This method allows multiple directives to be stored persistently and processed iteratively in batch mode. Each service configuration directive in each configuration file is executed in the order they are specified.

process_directive()

Process a single directive passed as a string.This method allows directives to be created dynamically and processed interactively, such as via a GUI or a network connection.

The following table summarizes the service configuration directives that can be processed by these two ACE_Service_Config methods:

以下是ACE_Service_Config能够处理的服务配置指令。

Directive

Description

dynamic

Dynamically link a service and initialize it by calling its init() hook method.动态连接

static

Call the init() hook method to initialize a service that was linked statically.静态连接

remove

Remove a service completely, that is, call its fini() hook method and unlink it from the application process when it's no longer used.删除服务

suspend

Call a service's suspend() hook method to pause it without removing it.挂起服务

resume

Call a service's resume() hook method to continue processing a service that was suspended earlier.恢复服务

stream

Initialize an ordered list of hierarchically related modules.

We describe the syntax and semantics for the tokens in each of these directives below.

Dynamically link and initialize a service: dynamic svc-name svc-type DLL-name:factory_func() [ "argc/argv options"]

The dynamic directive instructs the ACE Service Configurator framework to dynamically link and initialize a service object. The svc-name is the name assigned to the service. The svc-type designates the type of the service, which can be a Service_Object *, Module *, or Stream *. DLL-name is the name of the dynamic link library that contains the factory_func() symbol. This symbol is the entry point for an extern "C" function that the ACE_Service_Config interpreter invokes to create an instance of a service. If the svc-type is Service_Object * then factory_func() must return a pointer to an object derived from ACE_Service_Object. The factory_func() symbol should have a leading underscore character since linkers add that character to externally visible symbols.

DLL-name can be either a full pathname or a filename without a suffix. If it's a full pathname, the ACE_DLL::open() method described in Sidebar 33 is used to dynamically link the designated file into the application process. If it's a filename, however, ACE_DLL::open() uses ACE::ldfind() (also described in Sidebar 33) to locate the DLL and dynamically link it into the address space of the process via ACE_DLL::open(). The dynamic directive can be used portably across operating systems since ACE encapsulates these platform details.

The argc/argv options are an optional list of parameters that can be supplied to initialize a service object via its init() hook method. The ACE Service Configurator framework uses the ACE_ARGV class described in Sidebar 35 (page 148) to separate the string into arguments and substitute the values of environment variables that are included in the string.

Initialize a statically linked service: static svc-name ["argc/argv options"]

Although ACE_Service_Config is commonly used to configure services dynamically, it can also be used to configure services statically via the static directive. The svc-name and optional argc/argv options are the same as those in the dynamic directive. The syntax is simpler, however, since the service object must already be linked into the executable program image statically. Thus, there's no need to either locate and link a DLL or call a factory function to create a service object. Static configuration trades flexibility for increased security, which may be useful for certain types of servers that must contain only trusted, statically linked services. Recall from Sidebar 31 (page 132) that static service loading is disabled by default. Static service loading must therefore be enabled explicitly to use them, or else the static directive will have no effect.

Sidebar 33: The ACE_DLL Class

Applications that link and unlink DLLs explicitly can encounter the following problems:

- A nonuniform programming interface that's even less portable than the Socket API described in Chapter 3 of C++NPv1

- Unsafe types that invite errors and misuse because the native OS DLL APIs return weakly typed handles that are passed to DLL functions, such as those used to locate symbols and unlink the DLL

- Potential resource leaks, since it's possible to forget to release DLL handles

To address these problems, ACE defines the ACE_DLL wrapper facade class to encapsulate explicit linking/unlinking functionality. This class eliminates the need for applications to use error-prone, weakly typed handles and also ensures that resources are released properly by its destructor. In addition, it uses the ACE::ldfind() method to locate DLLs via the following algorithms:

DLL filename expansion?ACE::ldfind() determines the name of the DLL by adding the appropriate prefix and suffix. For example, it adds the lib prefix and .so suffix for Solaris and the .dll suffix for Windows.

DLL search path?ACE::ldfind() will also search for the designated DLL using the platform's DLL search path environment variable. For example, it searches for DLLs using LD_LIBRARY_PATH on many UNIX systems and PATH on Windows.

The key methods in the ACE_DLL class are outlined in the following table.

ACE Class

Description

ACE_DLL() open()

Opens and dynamically links a designated DLL

ACE_DLL() close()

Closes and optionally unlinks the DLL

symbol()

Returns a pointer to a function or object in the DLL

error()

Returns a string explaining which failure occurred

The interface of ACE_DLL is shown in the figure below.

ACE_DLL - handle_ : ACE_SHLIB_HANDLE + open (name : const ACE_TCHAR *, mode : int = ACE_DEFAULT_SHLIB_MODE, close_on_destruct : int = 1) : int + close () : int + symbol (name : const ACE_TCHAR *) : void * + error (void) : ACE_TCHAR *

Remove a service completely: remove svc-name

The remove directive causes the ACE_Service_Config interpreter to query the ACE_Service_Repository for the named service. If this service is located, the interpreter invokes its fini() hook method, which performs the activities needed to clean up resources when the service shuts down. If a service destruction function pointer is associated with the service object, it's called to destroy the service object itself (the ACE_FACTORY_DEFINE macro defines this function automatically). Finally, if the service was linked dynamically from a DLL, it's unlinked via the ACE_DLL::close() method. Since a DLL can be linked multiple times in a process, ACE_DLL::close() ensures that the DLL is only unlinked when it's no longer in use.

Suspend a service without removing it: suspend svc-name

The suspend directive causes the ACE_Service_Config interpreter to query the ACE_Service_Repository for the designated svc-name service. If this service is located, its suspend() hook method is invoked. A service can override this method to implement the appropriate actions needed to suspend its processing.

Resume a previously suspended service: resume svc-name

The resume directive causes the ACE_Service_Config interpreter to query the ACE_Service_Repository for the designated svc-name service. If this service is located, its resume() hook method is invoked. A service can override this method to implement the appropriate actions needed to resume its processing, which typically reverse the effects of the suspend() method.

Initialize an ordered list of hierarchically related modules: stream svc-name '{' module-list '}'

The stream directive causes the ACE_Service_Config interpreter to initialize an ordered list of hierarchically related modules. Each module consists of a pair of services that are interconnected and communicate by passing ACE_Message_Block objects. The implementation of the stream directive uses the ACE Streams framework described in Chapter 9.

The complete Backus/Naur Format (BNF) syntax for svc.conf files parsed by the ACE_Service_Config is shown in Figure 5.7. Sidebar 34 (page 146) describes how to specify svc.conf files using the optional XML syntax.

3. Utility methods. ACE_Service_Config defines the following utility methods:

Method

Description

reconfigure()

Reprocess the current configuration file(s).

suspend()

Suspend a service, identified by name.

resume()

Resume a suspended service, identified by name.

Figure 5.7 BNF for the ACE_Service_Config Scripting Language

<svc-conf-entries> ::= <svc-conf-entries> <svc-conf-entry> | NULL <svc-conf-entry> ::= <dynamic> | <static> | <suspend> | <resume> | <remove> | <stream> <dynamic> ::= dynamic <svc-location> <parameters-opt> <static> ::= static <svc-name> <parameters-opt> <suspend> ::= suspend <svc-name> <resume> ::= resume <svc-name> <remove> ::= remove <svc-name> <stream> ::= stream <streamdef> '{' <module-list> '}' <streamdef> ::= <svc-name> | dynamic | static <module-list> ::= <module-list> <module> | NULL <module> ::= <dynamic> | <static> | <suspend> | <resume> | <remove> <svc-location> ::= <svc-name> <svc-type> <svc-factory> <status> <svc-type> ::= Service_Object '*' | Module '*' | Stream '*' | NULL <svc-factory> ::= PATHNAME ':' FUNCTION '(' ')' <svc-name> ::= STRING <status> ::= active | inactive | NULL <parameters-opt> ::= '"' STRING '"' | NULL

The reconfigure() method can be used to force the ACE_Service_Config interpreter to reprocess the service configuration files. This capability is useful if your application monitors the service configuration files for changes. When changes are noticed, they can be processed using the reconfigure() method. This method is the recommended way to make run-time changes on Windows since there's no equivalent to the common UNIX practice of sending a signal, such as SIGHUP, to a process.

The suspend() and resume() methods enable the suspension and resumption of a service if its name is known. It's a shortcut to the methods defined in the ACE_Service_Repository singleton.

Example

This example shows how to apply the ACE Service Configurator framework to create a server whose initial configuration behaves as follows:

It statically configures an instance of Service_Reporter.

It dynamically links and configures the Reactor_Logging_Server_Adapter template from the Example portion of Section 5.2 into the server's address space.

We then show how to dynamically reconfigure the server to support a different implementation of a reactive logging service.

Sidebar 34: Using XML to Configure Services

The ACE_Service_Config class can be configured to interpret an XML-based scripting language. The Document Type Definition (DTD) for this language is shown below:

<!ELEMENT ACE_Svc_Conf (dynamic|static|suspend|resume |remove|stream|streamdef)*> <!ELEMENT streamdef ((dynamic|static),module)> <!ATTLIST streamdef id IDREF #REQUIRED> <!ELEMENT module (dynamic|static|suspend|resume|remove)+> <!ELEMENT stream (module)> <!ATTLIST stream id IDREF #REQUIRED> <!ELEMENT dynamic (initializer)> <!ATTLIST dynamic id ID #REQUIRED status (active|inactive) "active" type (module|service_object|stream) #REQUIRED> <!ELEMENT initializer EMPTY> <!ATTLIST initializer init CDATA #REQUIRED path CDATA #IMPLIED params CDATA #IMPLIED> <!ELEMENT static EMPTY> <!ATTLIST static id ID #REQUIRED params CDATA #IMPLIED> <!ELEMENT suspend EMPTY> <!ATTLIST suspend id IDREF #REQUIRED> <!ELEMENT resume EMPTY> <!ATTLIST resume id IDREF #REQUIRED> <!ELEMENT remove EMPTY> <!ATTLIST remove id IDREF #REQUIRED>

The syntax of this XML-based configuration language is different from the one in Figure 5.5, but its semantics are the same. Although it's more verbose to compose, the ACE XML-based configuration file format is more flexible. For example, users can plug in customized XML event handlers to extend the behavior of the ACE Service Configurator framework without modifying the underlying ACE implementation.

The XML configuration file format is relatively new (it was introduced in ACE 5.3). It's therefore not yet the default used by the ACE Service Configurator framework. Users can choose the XML-based Service Configurator by compiling ACE with the ACE_HAS_XML_SVC_CONF macro enabled. ACE provides the svcconf-convert.pl perl script to translate original format files into the new XML format. The script is located in the $ACE_ROOT/bin/ directory.

Initial server configuration. We start by writing the following generic main() program in Configurable_Logging_Server.cpp. This program configures the Service_Reporter and Reactor_Logging_Server_Adapter services into an application process and then runs the reactor's event loop.

1 #include "ace/OS.h" 2 #include "ace/Service_Config.h" 3 #include "ace/Reactor.h" 4 5 int ACE_TMAIN (int argc, ACE_TCHAR *argv[]) { 6 ACE_STATIC_SVC_REGISTER (Reporter); 7 8 ACE_Service_Config::open 9 (argc, argv, ACE_DEFAULT_LOGGER_KEY, 0); 10 11 ACE_Reactor::instance ()->run_reactor_event_loop (); 12 return 0; 13 }

Lines 1? There are no service-specific header files (or code) in the main() program. It's therefore completely generic, and can be reused for many programs that are configured using the ACE Service Configurator and Reactor frameworks.

Line 5 Replace the main() entry point name with the ACE_TMAIN macro. This macro uses the alternate wmain() entry point on Windows when Unicode is enabled, and the usual main() in all other situations.

Line 6 Register the static Reporter service with the ACE Service Configurator framework. Although the framework now knows of the service, it is not activated unless a service configuration directive causes it to be.

注册一个静态的Reporter服务到ACE_Service Configurator framework。虽然框架知道这个服务,但是他没有被激活除非服务配置指令激活他。

#if defined(ACE_LACKS_STATIC_CONSTRUCTORS)
# define ACE_STATIC_SVC_REQUIRE(SERVICE_CLASS)\
class ACE_Static_Svc_##SERVICE_CLASS {\
public:\
  ACE_Static_Svc_##SERVICE_CLASS() { \
   ACE_Service_Config::static_svcs ()->insert (\
    &ace_svc_desc_##SERVICE_CLASS); \
  } \
};
#define ACE_STATIC_SVC_REGISTER(SERVICE_CLASS)\
ACE_Static_Svc_##SERVICE_CLASS ace_static_svc_##SERVICE_CLASS

#else /* !ACE_LACKS_STATIC_CONSTRUCTORS */

# define ACE_STATIC_SVC_REQUIRE(SERVICE_CLASS)\
class ACE_Static_Svc_##SERVICE_CLASS {\
public:\
  ACE_Static_Svc_##SERVICE_CLASS() { \
   ACE_Service_Config::static_svcs ()->insert (\
    &ace_svc_desc_##SERVICE_CLASS); \
  } \
};\
static ACE_Static_Svc_##SERVICE_CLASS ace_static_svc_##SERVICE_CLASS;
#define ACE_STATIC_SVC_REGISTER(SERVICE_CLASS) do {} while (0)

#endif /* !ACE_LACKS_STATIC_CONSTRUCTORS */

Lines 8? Call ACE_Service_Config::open() to configure the application. All decisions about which service(s) to load, and all of the service parameters, are located in the service configuration file, which is external to the binary application program. We then run the reactor's event loop to handle I/O events from clients.

调用ACE_Service_Config::Open来配置程序,所有服务载入时的指令和服务参数都位于二进制程序外部的配置文件,然后我们处理reactor事件循环来处理来自客户端的I/O事件。

Since we know that our program will activate the static Service_Reporter service, we added the fourth argument (and by necessity, the third) to ACE_Service_Config::open() to explicitly enable static service loading. If we instead decided to leave this decision to the user or administrator, that argument would not be supplied, and the user would choose to enable or disable static services by supplying the -y option on the command line.

因为我们知道程序会激活静态服务,所以第四个参数设置为0,明确使静态服务可以载入。如果我们把这个决定留给用户或者管理员,他们可以在命令行数-y来加载静态服务。

When ACE_Service_Config::open() is called, it uses the ACE_Service_Config::process_directives() method to interpret the svc.conf file below:

1 static Service_Reporter "-p $SERVICE_REPORTER_PORT" 2 3 dynamic Server_Logging_Daemon Service_Object * 4 SLD:_make_Server_Logging_Daemon() 5 "$SERVER_LOGGING_DAEMON_PORT"

Line 1 The Service_Reporter code, registration information, and factory function were all statically linked into the executable program. This directive therefore simply causes the Service Configurator framework to activate the service by calling Service_Reporter::init() (page 132). The argc/argv arguments passed to init() are the string "-p" and an expansion of the SERVICE_REPORTER_PORT environment variable. The Service Configurator framework expands this environment variable automatically using the ACE_ARGV class described in Sidebar 35. ACE_ARGV recognizes SERVICE_REPORTER_PORT as an environment variable by its leading $ character and substitutes its associated value. The ACE_STATIC_SVC_REQUIRE macro used in Service_Reporter.cpp (page 135) ensures the Service_Reporter is registered with ACE_Service_Repository before the ACE_Service_Config::open() method is called.

第一行The Service_Reporter code,注册信息,工厂函数都静态的连接进可执行文件。指令引起Service Configurator framework通过调用Service_Reporter::init()来激活服务。argc/argv参数通过-p传递进init()和SERVICE_REPORTER_PORT环境变量的扩展。Service Configurator framework是用ACE_ARGV类来扩展环境变量。ACE-ARGV通过变量前的$来辨认环境变量,替代相关联得值。ACE_STATIC_SVCREQUIRE宏保证了Service_Reporter在ACE_Service_Config::open()调用之前在ACE_Service_Repository中注册。

Sidebar 35: The ACE_ARGV Class

The ACE_ARGV class is a useful utility class that can

Transform a string into an argc/argv-style vector of strings

Incrementally assemble a set of strings into an argc/argv vector

Transform an argc/argv-style vector into a string

During the transformation, the class can substitute environment variable values for each $-delimited environment variable name encountered. ACE_ARGV provides an easy and efficient mechanism to create arbitrary command-line arguments. Consider its use whenever command-line processing is required, especially when environment variable substitution is desirable. ACE uses ACE_ARGV extensively, particularly in its Service Configurator framework.

Lines 3? This code configures the server logging daemon via the following steps:

Dynamically link the SLD DLL into the address space of the process.

Use the ACE_DLL class described in Sidebar 33 (page 143) to extract the _make_Server_Logging_Daemon() factory function from the SLD DLL symbol table.

The factory function is called to allocate a Server_Logging_Daemon object.

The Service Configurator framework calls the service object's Server_Logging_Daemon::init() hook method, passing as its argc/argv arguments an expansion of the SERVER_LOGGING_DAEMON_PORT environment variable that designates the port number where the server logging daemon listens for client connection requests.

If init() succeeds, the Server_Logging_Daemon pointer is stored in the ACE_Service_Repository under the name "Server_Logging_Daemon".

动态连接SLD.DLL到进程地址空间。从SLD.DLL符号标中取出工厂函数 _make_Server_Logging_Daemon(),调用工厂函数创建Server_Logging_Daemon对象。Service Configurator 框架调用Server_Logging_Daemon::init() ,传递SERVER_LOGGING_DAEMON_PORT作为argc/argv。

Sidebar 36 illustrates an XML version of the svc.conf file shown above.

Sidebar 36: An XML svc.conf File Example

The XML representation of the svc.conf file shown on page 147 is shown below:

1 <ACE_Svc_Conf> 2 <static id='Service_Reporter' 3 params='-p $SERVICE_REPORTER_PORT'/> 4 5 <dynamic id='Server_Logging_Daemon' 6 type='service_object'> 7 <initializer path='SLD' 8 init='_make_Server_Logging_Daemon' 9 params='$SERVER_LOGGING_DAEMON_PORT'/> 10 </dynamic> 11 </ACE_Svc_Conf>

The XML svc.conf file is more verbose than the original format since it specifies field names explicitly. However, the XML format allows svc.conf files to express expanded capabilities, since new sections and fields can be added without affecting existing syntax. There's also no threat to backwards compatibility, as might occur if fields were added to the original format or the field order changed.

The SLD_DLL is generated from the following SLD.cpp file:

#include "Reactor_Logging_Server_Adapter.h" #include "Logging_Acceptor.h" #include "SLD_export.h" typedef Reactor_Logging_Server_Adapter<Logging_Acceptor> Server_Logging_Daemon; ACE_FACTORY_DEFINE (SLD, Server_Logging_Daemon)

The SLD.cpp file contains the Server_Logging_Daemon type definition that instantiates the Reactor_Logging_Server_Adapter template with the Logging_Acceptor class (page 54). The ACE_FACTORY_DEFINE macro generates the _make_Server_Logging_Daemon() factory function in the DLL containing the service code. If code outside the service DLL needs to refer to the factory function, it can use the ACE_FACTORY_DECLARE macro to declare the _make_Server_Logging_Daemon() factory function with the proper import declaration.

SLD.CPP包含了Server_Logging_Daemon 类型定义,通过模板参数Logging_Acceptor类实例化Reactor_Logging_Server_Adapter。ACE_FACTORY_DEFINE宏产生了包含服务代码的DLL中的_make_Server_Logging_Daemon工厂函数。如果代码在服务DLL的外部,needs to refer to factory function,它可以使用ACE_FACTORY_DECLARE宏来声明_make_Server_Logging_Daemon()工厂函数通过合适的导入声明。

ACE's import/export helper macros are described in Sidebar 37 (page 150). These macros help to ensure that a DLL's externally visible symbols are exported properly from the DLL on all supported platforms, as well as allowing the DLL's users to import them properly. Applying the export macros within the ACE service macros allow the ACE Service Configurator framework to look up the factory function's entry point symbol when activating a service.

ACE导入导出宏在Sidebar37中描述。这些宏保证dll的外部可见符号标从dll被合适的导出到所有支持的平台,同样运行dll的使用折合适的导入他们。激活一个服务时,应用导出宏运行ACE_Service Configurator framework来查找工厂函数的入口符号。

Sidebar 37: The ACE DLL Import/Export Macros

Windows has specific rules for explicitly importing and exporting symbols in DLLs. Developers with a UNIX background may not have encountered these rules in the past, but they are important for managing symbol usage in DLLs on Windows. ACE makes it easy to conform to these rules by supplying a script that generates the necessary import/export declarations and a set of guidelines for using them successfully. To ease porting, the following procedure can be used on all platforms that ACE runs on:

Select a concise mnemonic for each DLL to be built.

Run the $ACE_ROOT/bin/generate_export_file.pl Perl script, specifying the DLL's mnemonic on the command line. The script will generate a platform-independent header file and write it to the standard output. Redirect the output to a file named <mnemonic>_export.h

#include the generated file in each DLL source file that declares a globally visible class or symbol.

To use in a class declaration, insert the keyword <mnemonic>_Export between class and the class name.

When compiling the source code for the DLL, define the macro <mnemonic>_BUILD_DLL.

Following this procedure results in the following behavior on Windows:

Symbols decorated using the above guidelines will be declared using __declspec(dllexport) when built in their DLL

When referenced from components outside the DLL, the symbols will be declared __declspec(dllimport).

If you choose a separate mnemonic for each DLL and use them consistently, it will be straightforward to build and use DLLs across all OS platforms.

The UML sequence diagram in Figure 5.8 illustrates the steps involved in configuring the server logging daemon based on the svc.conf file shown above. At program start time, the object generated by the ACE_STATIC_SVC_REQUIRE macro registers the Service_Reporter information created using the ACE_STATIC_SVC_DEFINE macro into ACE_Service_Config. When the ACE_Service_Config::open() method is called it uses the specified factory function to instantiate a Service_Reporter object, but doesn't activate it. The open() method then calls process_directives(), which interprets the directives in the svc.conf file. The first directive activates the static Service_Reporter service. The second directive triggers the following actions:

Figure 5.8. UML Sequence Diagram for Configuring the Logging Server

The SLD DLL is linked dynamically.

The _make_Server_Logging_Daemon factory function is called to create an instance of Reactor_Logging_Server_Adapter.

The new service object's init() method is called to activate the service.

When all configuration activities are done, the main() program calls ACE_Reactor::run_reactor_event_loop(). At that point, the services are running, just like the objects that were configured statically in previous examples.

Reconfiguring the server. The ACE Service Configurator framework can reconfigure a server at run time in response to external events, such as signals or commands. At this point, the framework rereads its svc.conf file(s) and performs the designated directives, such as inserting or removing service objects into or from a server, and suspending or resuming existing service objects. We now illustrate how to use these features to reconfigure our server logging daemon dynamically.

ACE Service Configurator framework 通过响应外部事件可以在运行时重配置server例如signals或者commands。这时框架重新读svc.conf配置执行指定的指令,例如插入或者删除服务对象,挂起或者恢复存在的服务对象。我们解释一下如何使用这些特色来动态重配置我们的logging daemon。

The initial configuration of the logging server has the following limitations:

最初的配置有以下局限

It uses the Logging_Acceptor implementation from Section 3.3 (page 54), which doesn't time out logging handlers that remain idle for long periods of time.他使用Logging_Acceptor实现,logging handlers保持很长一段时间的空闲不超时。

There is no way to shut down the run_reactor_event_loop() method called on the ACE_Reactor singleton.没有办法关闭run_reactor_event_loop()方法。

We can add these capabilities without affecting existing code or the Service_Reporter service in the process by defining a new svc.conf file and instructing the server to reconfigure itself by sending it a signal, such as SIGHUP or SIGINT.

我们增加这些性能不影响存在的代码或者进程中的Service_Reporter服务通过定义一个新的svc.conf文件和通过发送一个signal例如SIGHUP或者SIGINT来让服务重新配置。

1 remove Server_Logging_Daemon 2 3 dynamic Server_Logging_Daemon Service_Object * 4 SLDex:_make_Server_Logging_Daemon_Ex() 5 "$SERVER_LOGGING_DAEMON_PORT" 6 7 dynamic Server_Shutdown Service_Object * 8 SLDex:_make_Server_Shutdown()

This svc.conf file assumes the server process is currently running with the Server_Logging_Daemon already configured. The ACE Service Configurator framework provides configuration mechanisms and assumes the policies that determine when and what to reconfigure are handled by an administrator or another application.

这个文件假设服务进程中已经运行着已经配置过的Server_Logging_Daemon。

Line 1 Remove the existing server logging daemon from the ACE service repository and unlink it from the application's address space.

Lines 3? Dynamically configure a different instantiation of the Reactor_Logging_Server_Adapter template into the address space of the server logging daemon. In particular, the _make_Server_Logging_Daemon_Ex() factory function is created by the ACE_FACTORY_DEFINE macro shown below in the SLDex.cpp file, which is used to generate the SLDex DLL.

typedef Reactor_Logging_Server_Adapter<Logging_Acceptor_Ex> Server_Logging_Daemon_Ex; ACE_FACTORY_DEFINE (SLDEX, Server_Logging_Daemon_Ex)

This macro instantiates the Reactor_Logging_Server_Adapter template with the Logging_Acceptor_Ex (page 67).

Lines 7? Dynamically configure a Server_Shutdown service object that uses the controller() function (page 98) and Quit_Handler class (page 98) to wait for an administrator to shut down the server via a command on its standard input. The Server_Shutdown class shown below inherits from ACE_Service_Object so that we can manage its life cycle via the ACE Service Configurator framework.

class Server_Shutdown : public ACE_Service_Object { public: Server_Shutdown::init() spawns a thread to run the controller() function: virtual int init (int, ACE_TCHAR *[]) { reactor_ = ACE_Reactor::instance (); return ACE_Thread_Manager::instance ()->spawn (controller, reactor_, THR_DETACHED); }

We pass the THR_DETACHED flag to spawn so that the controller's thread identifier and other resources are reclaimed by the OS automatically after the thread terminates.

The Server_Shutdown::fini() method notifies the reactor to shut down:

virtual int fini () { Quit_Handler *quit_handler = 0; ACE_NEW_RETURN (quit_handler, Quit_Handler (reactor_), -1); return reactor_->notify (quit_handler); } // ... Other method omitted ... private: ACE_Reactor *reactor_; };

We use ACE_FACTORY_DEFINE to generate the _make_Server_Shutdown() factory function needed by the ACE Service Configurator framework.

我们使用ACE_FACTORY_DEFINE来产生_make_Server_Shutdown()工厂函数。


# define ACE_FACTORY_DEFINE(CLS,SERVICE_CLASS) \
void _gobble_##SERVICE_CLASS (void *p) { \
  ACE_Service_Object *_p = static_cast<ACE_Service_Object *> (p); \
  ACE_ASSERT (_p != 0); \
  delete _p; } \
extern "C" CLS##_Export ACE_Service_Object *\
_make_##SERVICE_CLASS (ACE_Service_Object_Exterminator *gobbler) \
{ \
  ACE_TRACE (#SERVICE_CLASS); \
  if (gobbler != 0) \
    *gobbler = (ACE_Service_Object_Exterminator) _gobble_##SERVICE_CLASS; \
  return new SERVICE_CLASS; \
}

ACE_FACTORY_DEFINE (SLDEX, Server_Shutdown)

The UML sequence diagram in Figure 5.9 illustrates the steps involved in reconfiguring the server logging daemon based on the svc.conf file shown above.

Figure 5.9. UML Sequence Diagram for Reconfiguring the Logging Server

The dynamic reconfiguration mechanism in the ACE Service Configurator framework enables developers to modify server functionality or fine-tune performance without extensive redevelopment and reinstallation effort. For example, debugging a faulty implementation of the logging service simply involves the dynamic reconfiguration of a functionally equivalent service that contains additional instrumentation to help identify the erroneous behavior. This reconfiguration process may be performed without modifying, recompiling, relinking, or restarting the currently executing server logging daemon. In particular, this reconfiguration doesn't affect the Service_Reporter that was configured statically.

动态重配置机制使开发人员修改功能或者性能不用花费昂贵的重开发重安装的代价。 例如,调试一个logging服务的缺点简单的动态重配置一个功能相等的包含鉴别错误的服务。这种重配置进程不需要修改,编译,连接,重启动当前执行的server logging daemon。尤其静态配置的Service_Reporter。

Ru-Brd

本文地址:http://com.8s8s.com/it/it25278.htm