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源碼