一.概要
一般而言, Ice::Application 类对 于 Ice 客 户 和服 务 器来 说 已经 非常方便 , 但在有些情况下, 应 用可能需要作 为 Unix 看守( daemon )或 Win32 服 务 运行在系 统 一 级 。 对 于 这样 的情况, Ice 提供了 Ice::Service 。 一个可与 Ice::Application 相比的 单 体 类 ,但它 还 封装了低 级 的、 针对 特定平台的初始化和关 闭 步 骤 ——系 统 服 务 常常需要使用 这样 的步 骤 。
二 . 和 Ice::Application 的异同
1. Ice::Application 和 Ice::Service 的功能类似,抽象出模型也相近。
但Ice::Service 的含义是让一个 应用 用比 Ice::Application 更好的管理形式 ――“服务”的方式运行。
不会、也不需要在 Ice::Application 中包含多个 Ice::Service 。
2. Ice::Service 的需用户继承的核心函数为 start() , Ice::Application 需用户继承的核心函数为 run() ,不能混淆, 因为 Ice::Service也有一个run()函数 。
3. Ice ::Service 隐含了对 Service 的结束 ( 通过 run() 中调用 waitForShutdown () ),无需用户显式使用类似 ic-> waitForShutdown () 。 而 Ice::Application 必须主动在其 run() 函数中显式等待。可以说, Ice::Service 比 Ice::Application 提供了更贴心的服务,但换个角度来说, Ice::Application 更何尝不是给用户提供了更好灵活性和简洁性。
4. Ice::Application 对信号的支持要好于 Ice::Service 。双方都能过滤 SIGHUP 信号。
就代码而言,感觉是这两个类不是同一个人写的 。因为这两个类之间可互相补充的东西不少,甚至可以合并为同一个类。不知到ZeroC是基于何种考虑?
三 . 类成员
下面是 Ice::Service 类 的定 义 , 为方便起见 , 将所有的虚函数都并列在一起。
{
public :
Service();
virtual ~ Service();
// 关闭服务,默认操作是关闭服务所使用的Ice通信器
// 如果你需要其它的附加操作,继承并改写此函数
virtual bool shutdown();
// 信号回调函数,表示Service被信号所中断,默认操作是调用shutdown()函数
virtual void interrupt();
// 供给IceUtil::CtrlHandler注册的原始信号处理函数
// 它处理完SIGHUP后调用interrupt。一般无需继承和改写,除非你想自己处理SIGHUP
virtual void handleInterrupt( int );
protected :
// 允许子类执行它的启动活动,比如扫描所提供的参数向量、识别命令行选项,创建对象 // 适配器,以及注册servants。
// 如果启动成功,子类必须返回true,否则返回false。
// 一般必须继承,并在其中实现应用自己的应用逻辑。
virtual bool start( int , char * []) = 0 ;
// 阻塞等待Service的关闭。缺省操作是调用Ice通信器的waitForShutdown()。
virtual void waitForShutdown();
// 服务停止清理函数。决定了main()的返回值,缺省操作返回true。
// 如果你有其它资源需要清理,需要继承并改写该函数。
virtual bool stop();
// 初始化Service所用的Ice通信器
// Service类提供了一个方法让用户决定Ice通信器的构造方式
virtual Ice::CommunicatorPtr initializeCommunicator( int & , char * [], const InitializationData & );
// 日志相关函数
virtual void syserror( const std:: string & );
virtual void error( const std:: string & );
virtual void warning( const std:: string & );
virtual void trace( const std:: string & );
virtual void print( const std:: string & );
public :
// 服务入口函数,处理服务安装、卸载和run()的调用
int main( int & , char * [], const InitializationData & = InitializationData());
int main(StringSeq & , const InitializationData & = InitializationData());
//
// 返回服务使用的Ice通信器
Ice::CommunicatorPtr communicator() const ;
//
// 返回服务的惟一实例,注意实例是静态提供的。
static Service * instance();
//
// 判断当前运行模式。如果是以Win32服务或linux daemon方式运行,返回true.
// 否则返回false
bool service() const ;
// 返回当前Service名字,等同于Ice::Application::appName()
std:: string name() const ;
// 检查当前平台是否支持后台运行(win32服务/linux daemon)
bool checkSystem() const ;
// Service的真正执行函数
int run( int & , char * [], const InitializationData & = InitializationData());
protected :
// 允许信号中断
void enableInterrupt();
// 禁止信号中断
void disableInterrupt();
private :
Ice::LoggerPtr _logger; // Service使用的Log对象
Ice::CommunicatorPtr _communicator; // Services使用的通信器
bool _nohup; // 是否忽略SIGHUP函数
bool _service; // Service运行模式是否为服务/daemon
std:: string _name; // Service应用名
static Service * _instance; // Service的惟一静态实例
};
四.用法举例
虽然 Ice::Service 可改写的行为较多,但一般情况下,只需改写: start(), stop() 和 interrupt () 。继承 interrupt () 时 , 要么注意调用父类的 interrupt () 函数,否则自己处
理信号。
{
protected :
virtual bool start( int , char * []);
virtual bool stop();
virtual void interrupt();
private :
Ice::ObjectAdapterPtr m_adapter;
};
void MyService::interrupt()
{
std::cout << " Receive signal ... " << std::endl;
Ice::Service::interrupt();
}
bool MyService::stop()
{
std::cout << " Stop running ... " << std::endl;
return true ;
}
bool MyService::start( int argc, char * argv[])
{
std:: string endpoint = " tcp -p 10000:udp -p 10000 " ;
m_adapter = communicator() -> createObjectAdapterWithEndpoints( " MonitorAdapter " , endpoint);
Ice::ObjectPtr object = new CCheckFile;
m_adapter -> add( object ,communicator() -> stringToIdentity( " CheckFile " ));
m_adapter -> activate();
return true ;
}
int main( int argc, char * argv[])
{
MyService svc;
int ret = svc.main(argc, argv);
return ret;
}
五.作为后台服务 /daemon 运行
Ice::Service 提供的安装方式很简洁,这个功能也是 Ice::Service 的主要魅力所在。
1) Unix 看守
在 Unix 平台上, Ice::Service 能 识别 以下命令行 选项 :
• --daemon
• --noclose
• --nochdir
2) Win32 服务
在 Win32 平台上 2 ,如果指定了 --service 选项 , Ice::Service 会把 应 用作 为 Windows 服 务 启 动 ( 在 Windows 95/98/ME 上不支持 Windows 服务 )
• --service NAME
但是,在 应 用作 为 Windows 服 务 运行之前,它必 须 先被安装,因此 , Ice::Service 类还 支持另外一些的命令行 选项 ,用于 执 行管理活 动 :
• --install NAME [--display DISP] [--executable EXEC][ARG ...]
安装 NAME 服 务 。 如果指定了 --display 选项 ,就把 DISP 用作服 务 的 显 示名,否 则 就使用 NAME 。如果指定了 --executable 选项 ,就把 EXEC 用作服 务 的可 执 行路径名,否 则 就使用可 执 行文件的路径名来 调 用 --install 。 其他任何参数都会不加改 变 地 传给 Service::start 成 员 函数。注意,在启 动时传给 服 务 的参数集中, 这 个命令会自 动 增加命令行参数 --service NAME ,因此,你不需要 显 式地指定 这 些 选项 。
• --uninstall NAME
移除 NAME 服 务 。 如果服 务 目前是活 动 的,在反安装之前,必 须 先使它停止。
• --start NAME [ARG ...]
启 动 NAME 服 务 。其他任何参数都会不加改 变 地 传给 Service::start 成 员 函数。
• --stop NAME
停止 NAME 服 务 。如果指定的管理命令不止一个,或者在使用 --service 的同 时还 使用了管理命令 ,就会 发 生 错误 。在 执 行了管理命令之后,程序会立即 终 止。 Ice::Service 类 支持 Windows 服 务 控制代 码 SERVICE_CONTROL_INTERROGATE 和 SERVICE_CONTROL_STOP 。在收到 SERVICE_CONTROL_STOP 时 , Ice::Service 会 调 用 shutdown 成 员 函数。
六.代码分析
分析 run 函数和信号处理函数 handleInterrupt
1. run 函数
{
// run()被main()函数调用,在main()函数中解析用户参数并决定运行模式
// 决定在后台运行
if (_service)
{
#ifdef _WIN32
return runService(argc, argv, initData);
#else
return runDaemon(argc, argv, initData);
#endif
}
// 在前台运行
int status = EXIT_FAILURE;
try
{
// 设置信号捕捉器,其作用实现在对Application的分析文章中描述
_ctrlCHandler = new IceUtil::CtrlCHandler;
//
// 初始化Ice通信器, 注意这里你可以通过改写initializeCommunicator()
// 来控制Ice通信器的生成
_communicator = initializeCommunicator(argc, argv, initData);
// 有关log的设定
_logger = _communicator -> getLogger();
// 从配置读取Ice.Nohup,默认值为1。决定是否要忽略SIGHUP消息.
// 使得应用在用户注销或者shell退出后依然运行
_nohup = _communicator -> getProperties() -> getPropertyAsIntWithDefault( " Ice.Nohup " , 1 ) > 0 ;
// 执行用户的主体函数
if (start(argc, argv))
{
// 等待应用结束,注意Ice::Application不会等待结束。
waitForShutdown();
// 调用用户的清理函数
if (stop())
{
status = EXIT_SUCCESS;
}
}
}
catch ( const IceUtil::Exception & ex)
{
ostringstream ostr;
ostr << " service caught unhandled Ice exception: " << ex;
error(ostr.str());
}
catch ( const std::exception & ex)
{
ostringstream ostr;
ostr << " service caught unhandled std::exception: " << ex.what();
error(ostr.str());
}
catch ( const std:: string & msg)
{
ostringstream ostr;
ostr << " service caught unhandled exception: " << msg;
error(ostr.str());
}
catch ( const char * msg)
{
ostringstream ostr;
ostr << " service caught unhandled exception: " << msg;
error(ostr.str());
}
catch (...)
{
error( " service caught unhandled C++ exception " );
}
try
{
_communicator -> destroy();
}
catch (...)
{
}
return status;
}
2. handleInterrupt () 函数
{
#ifdef _WIN32
// 如果设置了Ice.Nohup,则忽略用户注销消息
if (_nohup && sig == CTRL_LOGOFF_EVENT)
{
return ;
}
#else
// 如果设置了Ice.Nohup,则忽略shell退出/用户注销消息
if (_nohup && sig == SIGHUP)
{
return ;
}
#endif
// 调用信号回调函数
interrupt();
}
注意, interrupt () 的默认实现是调用 shutdown() ,也就是关闭 Service 。因此默认情况下, Ctrl+C 就可以正常退出。
七.参考文献
Ice-1.3.0 中文手册(马维达,感谢他的无私贡献)
Ice-3.1.1 英文手册
Ice-3.2.1 源码