Ice笔记-利用Ice::Service类简化Ice应用
Ice笔记-利用Application类简化Ice应用
一.概要
一般而言,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
源码