ICE框架元件內部實現與特點

工程師WWW發表於2015-01-15

1,Ice::Application 此類中有一個main的方法,main的方法中呼叫了run()函式。

ICE框架的服務配置結構:

class ResTransferServer : public Ice::Application
{
public:

    virtual int run(int argc, char* argv[])
    {
        Ice::CommunicatorPtr communicator = this->communicator(); //得到一個通訊器指標
        Ice::ObjectAdapterPtr adapter = communicator->createObjectAdapter("PUSHResourceTransferSvrObjectAdapter"); //建立一個配置器指標
        Ice::PropertiesPtr properties = communicator->getProperties(); //建立一個通訊器的許可權
        PUSH::ResTransferPtr resTransSvr = new ResTransferI;
        adapter->add(resTransSvr, communicator->stringToIdentity(properties->getProperty("Identity"))); //增加一個介面卡
        adapter->activate();//激發啟動一個通訊器
        communicator->waitForShutdown();  //通訊器在這裡等待處理資料連線
        return EXIT_SUCCESS;
    }
};

int  main(int argc, char* argv[])
{
    Ice::InitializationData initData;  //初始化ICE相關的資料;原型是 Ice::initialize 
    initData.properties = Ice::createProperties(); //建立通訊器的屬性介面
    initData.properties->setProperty("BuildId", std::string("Ice ") + ICE_STRING_VERSION);//設定所有者的相關屬性,或者通訊器的所有屬性,ICE中屬性的原型介面為Properties()

    PUSHResTransferServer app; //實際的服務物件
    int status = app.main(argc, argv, initData);

 //這裡物件呼叫main的原因是公有繼承Ice::Application類中的mian函式

     return status;  //返回結果傳遞給主執行緒,使系統來處理其他業務
 }

 

2,ICE框架中slice生成的類都是抽象類,我們要重新繼承他實現對此類中資料及方法的例項化的類

3,在ICE的一部呼叫中特別注意的幾點,用具體的例項來說明

具體的函式實現引數說明

 virtual void downRes_async(const PUSH::AMD_ResTransfer_downResPtr& amdPtr, const ::PUSH::Wmmp3MsgHead&, const PUSH::Wmmp3MsgDownRes& request, const ::Ice::Current& = ::Ice::Current());

 

注意:

ICE的每個方法都提供了一個Ice::Current的物件,這個物件主要是儲存ICE框架中的各個骨架資訊的引數類的物件。例如,adapter介面卡,ctx資料內容的類物件等等。

具體結構如下:

module Ice {
    local dictionary<string, string> Context;
    enum OperationMode { Normal, \Nonmutating, \Idempotent };
    local struct Current {

        ObjectAdapter   adapter;
        Connection      con;
        Identity        id;
        string          facet;
        string          operation;
        OperationMode   mode;
        Context         ctx;
        int             requestId;
    };
};

 


4,此虛方法的實現:

 void ResTransferI::downRes_async(const PUSH::AMD_ResTransfer_downResPtr& amdPtr, const ::PUSH::Wmmp3MsgHead&, const ::PUSH::Wmmp3MsgDownRes& request, const ::Ice::Current& current) //這裡的引數是ICE框架中提供的引數用來傳遞
{

 amdPtr->ice_response(response); //提供回包的方法

 //Ice在每個代理方法之後,攜帶了一個引數Ice::Context,這個引數實際是一個Map結構。它是一種“給方法提供額外資料(與方法所代表的業務無關的)”的方式

    ::Ice::Context ctx = outcommPrx->ice_getContext();
    char range[100]= {0};
    ::sprintf(range,"bytes=%d-%d", request.offset,(int)request.blockSize + request.offset);
    ctx["url"] = httpUrl + attachinfo.file_save_name();//配網址
    ctx["method"] = "get";
    ctx["Range"] = range; //"xxx-xxx";
    ctx["outheader"] = "1";

 

}

 

 void ResTransferI::downRes_async()  //ICE的AMD非同步分派方法函式組成,格式:函式名_async

5,非同步呼叫的時候要使用回撥函式意思是先把資料傳送出去等有結果在在回國投來處理這個訊息;

回撥函式的有2個部分組成:第一個是回撥方法,第二個是回撥資訊 。

回撥方法類如下:

class ResTransferCallback : public IceUtil::Shared //紅色的是ICE中提供的類
    {
    public:
         void exception(const Ice::Exception& ex, const DSE::AMDCallbackCookiePtr& cookie)
         {
             cookie->getAMDCallback()->ice_exception(ex);   //捕捉異常的方法
         }

         void sent(bool sentSynchronously, const DSE::AMDCallbackCookiePtr& cookie)
         {
         }

//執行回撥的方法函式,回撥之後的問題是相關返回引數資料的獲取,一般我們將這些信心儲存在cookie類中,例如下面的DSE::AMDCallbackCookiePtr這個類就是用來儲存我們需要的返回資料的。
         void downResResponse(const Ice::ByteSeq & respContent, const PUSH::HeaderMap & headers, const DSE::AMDCallbackCookiePtr& cookie);
    };

//將一個類轉化為這個類的智慧指標方法格式化,這是ICE裡面提供的方法。

 typedef ::IceUtil::Handle<ResTransferCallback> ResTransferCallbackPtr;


回撥資訊的儲存類是如下:

class AMDResTransferCallbackCookie : public ::DSE::AMDCallbackCookie//紅色的是ICE的callback實現的類 
    {
    public:
        AMDResTransferCallbackCookie(const AMDCallbackPtr& amdPtr)
        :AMDCallbackCookie(amdPtr)
        {}  
  
        void setTlvs(const ::PUSH::TlvMap & tlvs)
        {
            _tlvs = tlvs;
        }
        ::PUSH::TlvMap getTlvs( )
        {
   return  _tlvs;
        }

        void setTransId(const int & TransId)
        {
            _TransId = TransId;
        }
        int getTransId()
        {
          return  _TransId;
        }

    private:
        int _TransId;
  ::PUSH::TlvMap _tlvs;
    };
    //ICE中將抽象類轉化為智慧指標
    typedef ::IceUtil::Handle<AMDResTransferCallbackCookie> AMDResTransferCallbackCookiePtr;

 6,ice通過cookie專遞需要返回的資料存放在AMDcallbackCookie的類中,下來就是關於cookie類的智慧指標轉化為一般的AMDcallbackCookie類的物件

//dynamicCast(cookie);貌似是一種轉化的方式
 
    AMDResTransferCallbackCookiePtr myCookie = AMDResTransferCallbackCookiePtr::dynamicCast(cookie);
    AMD_ResTransfer_downResPtr amdPtr = AMD_ResTransfer_downResPtr::dynamicCast(myCookie->getAMDCallback()); 
 //具體體現還原智慧指標的方法。
    response.transId = myCookie->getTransId();

 

7,ICE的slice檔案內容事項:

sequence<Fruit> FruitPlatter;  //序列話一個型別為一個vector<char>容器變數。

dictionary<long, Employee> EmployeeMap; //將一個型別設定為一個map容器來處理。

標頭檔案只允許使用<>括號,註釋使用;結構中的返回值變數要賦值;常量要使用const修飾。

基本資料型別有:int , byte, ice::ByteSeq  bool, string,short ,long,float,double ;

左邊是輸入引數使用:in ,右邊是輸出引數使用:out ;

函式型別使用: void ;

slice檔案的中定義的結構中的資料成員是編譯之後就會產生對應的公有資料成員,函式方法都會成為純虛擬函式。

slice的事例檔案://紅色字型都是要注意的關鍵字

#include <SystemException.ice>

module TEST      //編譯後是名稱空間
{
    struct WmmpInfo
 {
        TlvMap tlvs; //此結構可以來自其他的slice檔案
 };
    sequence<  WmmpInfo> MsgAppSeq; //將來會成為vector容器
   

dictionary<string, string> HeaderMap; //將來會成為map容器
    struct Header
    {
        string transID;
       byte retcode = 0xFF;
    };

 interface AppClass  //抽象類名
    {

  ["ami"] void sendStateRpt( in  Header Header, out Response respMsg ) throws DSE::SystemException;  //ami表示同步,

 ["amd", "ami"] void downUrlInfo(  in   PushRequest reqMsg,  out Response respMsg )     throwsDSE::SystemException;  //amd表示非同步的方式
    };
}

                                     二,ICE片段講解

1,將資料寫入ICE流中 IceInternal::BasicStream stream(IceInternal::getInstance(_communicator).get());
 stream.resize(outParams.size());    stream.i = stream.b.begin();    memcpy(stream.b.begin(), &outParams[0], outParams.size());


2,介面卡包含通訊器獲取一個通訊器智慧指標
 Ice::CommunicatorPtr communicator = current.adapter->getCommunicator(); 你在任何時候都可以呼叫Communicator::getProperties,獲得特定通訊器的屬性集。
例如:從當前物件中獲取配置檔案的引數: 這裡所指的是通過ICE當前物件獲取的
Ice::PropertiesPtr properties = current.adapter->getCommunicator()->getProperties();
        std::string strURL = properties->getProperty("MsgStateRptUrl");

3,定義一個代理指標通過通訊器指標服務的識別符號獲取代理指標。
::Ice::ObjectPrx base = communicator->stringToProxy(PUSH_TDISPATCH_OUT_IDENTITY);  //communicator通訊器的只能指標
ice每一個服務物件都是通過身份確認的唯一標識來找到對應的服務同時呼叫相關的介面。例如:通過代理獲取內容引數物件方便從配置檔案中取得引數:
  Ice::Context ctx = outcommPrx->ice_getContext();    Ice::PropertiesPtr properties = communicator->getProperties();
    ctx["url"] = properties->getProperty("MsgStateRptUrl");

4,在建立通訊器前對引數進行設定setProperty 操作把某個屬性設成指定的值(只要把屬性設成空串,你就可以清除它)。只有在你呼叫initialize 之前,這個操作才有

5,獲取程式的程式id   id = IceUtil::generateUUID();

二,
1,slice 中的引數in是client傳給ICE物件的,out是ice物件代表的服務引數傳給client,client 要想和ICE的服務聯絡起來就需要一個ice的代理,
只有代理才可以獲取ICE對應服務的相關地址,

ICE 中編譯slice的方法:slice2cpp name.ice , slice中生成的都是抽象類,具體的服務我們還是需要繼承抽象類來具體例項化這些服務的方法的。
ice::exception   ICE根異常分為userexpection和localexpection
ICE中的資料型別在c++中首字母都是大寫的,例如Ice::Byte
IceProxy::Ice::Object  是繼承一個類,而ObjectPrx則是繼承一個代理類。 
Ice::share 
IceUtil::handle 建立一個智慧指標,總體來講是Ice的功能函式較多,比如方法,或者指標處理,字串處理等等。
Ice::中的物件可以直接與0進行比較的,對於不同的代理而言的話要知道2個代理是不是代表的同一個物件我們要使用Ice所提供的函式proxyIdentityEqual()身份確認函式。
所有的執行緒API 都是IceUtil 名字空間的一部分。IceUtil::Mutex 類互斥體,IceUtil::Time時間類,class ThreadControl 執行緒類。

IceBox 服務配置它負責載入和管理的應用特有的服務,並且可以對它進行遠地管理
IceStorm 釋出/ 訂閱服務,充當的是收集器( 釋出者) 與監視器( 訂閱者)之間的“中間人”,它提供了若干好處:
IceStorm 允許每個訂閱者指定它自己的服務質量 (QoS) 引數,IceStorm::QoS qos;是一個詞典即map,對其訊息的遞送施加影響。服務質量引數由一個含有名- 值對的詞典表示。
Topic是IceStorm的一個成員。Topic 介面表示一個主題,它提供了若干管理操作,可用於配置連結、管理訂閱者。
module IceStorm {
struct LinkInfo {
Topic* theTopic;
string name;
int cost;
};
sequence<LinkInfo> LinkInfoSeq;
dictionary<string, string> QoS;
exception LinkExists {
string name;
};
exception NoSuchLink {
string name;
};
interface Topic {
nonmutating string

2,AMDCallbackCookiePtr& cookie
dynamicCast()是封裝dynamic_cast<>方法,作用是:主要用於類層次間的上行轉換和下行轉換,還可以用於類之間的交叉轉換。
AMDResTransferCallbackCookiePtr myCookie = AMDResTransferCallbackCookiePtr::dynamicCast(cookie);

PUSH::AppServerOutcommPrx outcommPrx = PUSH::AppServerOutcommPrx::uncheckedCast(current.adapter->getCommunicator()->stringToProxy(identity));
    if(!outcommPrx)

所有的代理都派生自共同的IceProxy::Ice::Object類。這個代理基類含有一個方法,可用於建立單向代理,叫作ice_oneway:
注意,我們使用了uncheckedCast 來把代理從ObjectPrx 向下轉換成PersonPrx:對於單向代理,我們不能使用checkedCast,因為checkedCast 要求伺服器作出答覆,而單向代理

當然不允許那樣的答覆。如果你想要使用安全的向下轉換,你可以首先把雙向代理向下轉換成實際的物件型別,然後再獲取單向代理:PersonPrx onewayPerson

=PersonPrx::uncheckedCast(oneway);
然後呼叫通訊器的setStats():單向呼叫傳輸資料避免阻塞的話,推薦將in引數的方法和out引數的方法分開來。

3,Ice::Application封裝了所有正確的初始化和結束活動,它是啟動一個服務的開始方法的繼承類
#include <Ice/Ice.h>
class MyApplication : virtual public Ice::Application {
public:
virtual int run(int, char * []) {
// Server code here... 修改的部分
return 0;
}
};
int
main(int argc, char * argv[])
{
MyApplication app;
return app.main(argc, argv);
}

4,通訊器提供了一些操作: proxyToString , stringToProxy,這兩個操作允許你把代理轉換成串化表示,或進行反向轉換。
 createObjectAdapter, createObjectAdapterWithEndpoints 這兩個操作建立新的物件介面卡。每個物件介面卡都與一個或更多傳輸端點關聯在一起。
一個物件介面卡通常擁有一個傳輸端點。但是,一個物件介面卡也可以提供多個傳輸端點。

5,傳給伺服器端的每個骨架操作的最後一個引數(型別是Ice::Current)。Current 物件的定義是:
module Ice {
local dictionary<string, string> Context;
enum OperationMode { Normal, \Nonmutating, \Idempotent };
local struct Current {
ObjectAdapter adapter;
Identity id;
FacetPath facet;
string operation;
OperationMode mode;
Context ctx;
};
};

6,IcePack定位器服務伺服器啟用和監控服務,用於管理伺服器程式 ,Ice::Stats 介面,通訊器會把通訊流量(傳送和接收位元組數)告知該物件.

7,外掛是用於給通訊器增加特性的物件。例如, IceSSL 被實現成外掛。每個通訊器都有一個外掛管理器,這個管理器實現Ice::PluginManager 介面,通過它,
你可以訪問通訊器的外掛集。

三,

C++ 程式碼生成器為每個AMI 操作生成以下程式碼:
1. 一個抽象的回撥類, Ice run time 用它來通知應用,操作已完成。類名是按這樣的模式取的:AMI_class_op。例如,對於在介面I 中定義的名叫foo 的操作 ,
  對應的類的名字是AMI_I_foo。這個類所在的名字空間與操作所屬的介面或類相同。這個類提供了兩個方法:void ice_response(<params>);
  表明操作已成功完成。各個引數代表的是操作的返回值及out 引數。如果操作的有一個非void 返回型別, ice_response 方法的第一個引數就是操作的返回值。
  操作的所有out 引數都按照宣告時的次序,跟在返回值的後面。void ice_exception(const Ice::Exception &);表明丟擲了本地或使用者異常。

2. 一個額外的代理方法,其名字是操作在對映後的名字,加上_async。這個方法的返回型別是void,第一個引數是一個智慧指標,指向上面描述的回撥類的一個例項。
其他的引數由操作的各個in 引數組成,次序是宣告時的次序。


3,檢測ICE配置檔案。
 2. 為這個IcePack 節點建立資料庫目錄結構:
 $ mkdir db
 $ mkdir db/registry
 $ mkdir db/node
 3. 啟動這個IcePack 節點。注意,該節點將有一個並置的IcePack 登錄檔服務,因為在配置檔案中定義了IcePack.Node.CollocateRegistry 屬性。
 $ icepacknode --Ice.Config=config
4. 在另一個視窗中,啟動管理工具,部署該應用。我們假定,應用描述符在檔案application.xml 中。在啟動icepackadmin 時,我們把同一個配置檔案用作icepacknode。
 這樣做只是為了方便;管理工具只需要Ice.Default.Locator 屬性。
 $ icepackadmin --Ice.Config=config
 >>> application add "application.xml"
 現在,這個應用應該已經部署好了。注意, application.xml 必須放在引號中,因為在icepackadmin 命令語言中, application 是一個關鍵字。
 你可以使用下面的命令來驗證配置:
 >>> server list
 SimpleServer
 >>> adapter list
 IcePack.Node-node
 IcePack.Registry.Internal
 SimpleAdapter-SimpleServer
 >>> object find ::Simple
 SimpleServer -t @ SimpleAdapter-SimpleServer

5,配置路由器
下面的路由器配置屬性建立了必需的端點:
Glacier.Router.Endpoints=tcp -h 5.6.7.8 -p 8000
Glacier.Router.Client.Endpoints=tcp -h 5.6.7.8
Glacier.Router.Endpoints 所定義的端點叫做路由器控制端點,
因為Ice run time 會在客戶中使用它,直接與路由器互動。Glacier.Router.Client.Endpoints 所定義的端點是有路由的代理髮送的請求的目的地。
這些端點都必須能被客戶訪問,因此會在公共網路介面上定義1。路由器控制端點使用了一個固定的埠,因為客戶可能靜態配置了針對這個端點的代理。
而客戶端點不需要使用固定埠。

6,啟動路由器
假定24.3.1 節中給出的配置屬性儲存在名為config 的檔案中,你可以用下面的命令啟動路由器:
$ glacierrouter --Ice.Config=config

7,配置客戶
下面的屬性配置客戶中的所有代理,以使用Glacier 路由器:
Ice.Default.Router=Glacier/router:tcp -h 5.6.7.8 -p 8000
引數  name物件介面卡的名字。endpoints物件介面卡的端點。

相關文章