Ice筆記-利用Ice::Service類簡化Ice應用

Ice筆記-利用Ice::Service類簡化Ice應用

Ice筆記-利用Application類簡化Ice應用

 


一.概要

一般而言,Ice::Application 類對Ice 和服器來已經非常方便但在有些情況下,用可能需要作Unix 看守(daemon)或Win32 運行在系這樣的情況,Ice 提供了Ice::Service 一個可與Ice::Application 相比的,但它封裝了低的、 針對特定平臺的初始化和關——系常常需要使用這樣的步

 


Ice::Application的異同
1. Ice::ApplicationIce::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::ServiceIce::Application提供了更貼心的服務,但換個角度來說,Ice::Application更何嘗不是給用戶提供了更好靈活性和簡潔性。


4. Ice::Application對信號的支持要好於Ice::Service。雙方都能過濾SIGHUP信號。


就代碼而言,感覺是這兩個類不是同一個人寫的。因爲這兩個類之間可互相補充的東西不少,甚至可以合併爲同一個類。不知到ZeroC是基於何種考慮?

 


類成員

下面是Ice::Service 的定爲方便起見將所有的虛函數都並列在一起。

class ICE_API 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(intchar*[]) = 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()函數,否則自己處
理信號。

 

class MyService : public Ice::Service 
{
protected:
    
virtual bool start(intchar * []);
    
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

指明程序應該看守運行。涉及到建一個後臺子程,Service::main 將在個子程中行其任。在子程成功調start 函數之前,父程不會(shell 腳本啓動看守常常會帶來不確定性,上述行爲消除了這一不確定性,因爲它確保了命令調用不會在看守準備好接收請求之前就完成)。除非另外收到指示,否Ice::Service 會把子程的當前工作目錄變根目,並關所有無用的文件描述符。注意, 在通信器初始化之前,各文件描述符不會關,也就是,在時間裏,入、出,以及錯誤都可以使用。例如, IceSSL 插件可能需要在入上 提示入口令,而如果置了Ice.PrintProcessId Ice 可能要在出上打印子id


--noclose

阻止Ice::Service 無用的文件描述符。調試程中,可能會很有用,因 這樣一來,你就可以通看守的出和錯誤進出了。


--nochdir

阻止Ice::Service 更當前工作目

--noclose --nochdir 選項只能和--daemon 一起指定。在傳給start 函數的參數向量中,選項會被移除。

2) Win32 服務

Win32 平臺上2,如果指定了--service選項Ice::Service 會把用作Windows (Windows 95/98/ME 上不支持Windows 服務)


--service NAME

名叫NAME Windows 傳給start 函數的參數向量中,選項會被移除。


但是,在用作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函數

int Ice::Service::run(int& argc, char* argv[], const InitializationData& initData)
{
    
// 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()函數
void Ice::Service::handleInterrupt(int sig)
{
#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源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章