ICE中間件說明文檔node
1 ICE中間件簡介數據庫
2 平臺核心功能編程
2.1 接口描述語言(Slice)安全
2.2 ICE運行時服務器
2.2.1 通訊器網絡
2.2.2 對象適配器數據結構
2.2.3 位置透明性併發
2.3 異步編程模型app
2.3.1 異步方法調用負載均衡
2.3.2 異步方法分派
2.4 訂閱/發佈編程模型
2.5 動態服務管理(IceBox)
2.6 ICE網格計算
2.6.1 分佈式部署
2.6.2 負載均衡
2.6.3 註冊中心集羣
2.7 IceSSL應用
2.8 持久化存儲(IceFreeze)
3 ICE平臺功能研究小結
Ice 是 Internet Communications Engine 的簡稱,是一種面向對象的中間件平臺,支持面向對象的RPC編程,其最初的目的是爲了提供相似CORBA技術的強大功能,又能消除CORBA技術的複雜性。該平臺爲構建面向對象的客戶-服務器應用提供了工具、API 和庫支持。
由ICE平臺開發的應用支持跨平臺部署,多語言編程,其中服務端支持C++、JAVA、C#、Python等幾種編程語言,客戶端還支持Ruby、PHP。ICE支持同步/異步、訂閱/發佈的編程模式,支持分佈式部署,網格計算,內置負載均衡功能,支持SSL安全加密。
ICE 既能夠把TCP/IP、也能夠把UDP 用做底層傳輸機制, 還容許你把SSL 用做傳輸機制,讓客戶與服務器間的全部通訊都進行加密。
ICE平臺還提供一序列的編程庫,包括線程模型庫、定時器、信號處理器,這些編程庫的API所有支持線程安全。
ICE目前的最新版本爲ICE3.4.1,是2010年6月份發佈的,ICE3.1.1(2006年10月份發佈)及之前的版本支持Windows、LINUX、AIX、HP-UX、Solaris操做系統,可是ICE3.2.0(包含)之後的版本官方再也不保證對AIX的徹底支持,而3.3.1(包含)之後版本連HP-UX都不保證徹底支持,雖然源代碼中仍然提供了這兩個操做系統下的源代碼編譯文件Make.rules.AIX和 Make.rules.HP-UX,根據官方的論壇說明是由於他們基本上沒有這兩個系統的商業客戶,因此再也不對這兩個平臺進行代碼測試,若是有須要的須要和他們聯繫。
Slice (Specification Language for Ice)是一種用於使對象接口與其實現相分離的基礎性抽象機制。 Slice 在客戶與服務器之間創建合約,描述應用所使用的各類類型及對象接口。這種描述與實現語言無關,因此編寫客戶所用的語言是否與編寫服務器所用的語言相同,這沒有什麼關係。
Slice 定義由編譯器編譯到特定的實現語言。編譯器把與語言無關的定義翻譯成針對特定語言的類型定義和API。開發者使用這些類型和API 來提供應用功能,並與Ice 交互。目前ICE支持Slice到C++、JAVA、C#、Python、Ruby、PHP的語言映射。
Slice示例:
module Family //對應C++的名字空間
{
interface Child;
sequence<Child*> Children; // OK 能夠利用C++的Vector
interface Parent //對應C++的類
{
Children getChildren(); // OK
};
interface Child
{
Parent* getMother();
Parent* getFather();
};
};
利用Slice 定義接口/類方法時和其餘編程語言很類似,須要一個返回值和若干個參數,可是須要注意的是Slice不支持引用方式的參數傳遞,參數要麼爲輸入參數,要麼爲輸出參數,不一樣時爲in和out.做爲out參數的時候,無論客戶端對out參數的初始賦值是什麼,在服務端都取不到該,可是服務端能夠對該參數進行賦值,再傳遞給客戶端。例如:
interface Hello
{
string sayHello(int num,out string strMsg);
};
還要注意的是out參數必定是放在全部輸入參數後面,不容許交叉。
Ice run time 的主進入點由本地接口Ice::Communicator 表示,一般一個應用服務器只使用一個通訊器,一個通訊器實例管理着運行時資源,主要的運行時資源包括:
² 線程池:負責接收客戶端鏈接和請求。
² 配置屬性:Ice run time 的各個方面能夠經過屬性進行配置。每一個通訊器都有本身的配置屬性
² 插件管理器:插件是用於給通訊器增長特性的對象。例如, IceSSL (參見第23章)被實現成插件。每一個通訊器都有一個插件管理器,這個管理器實現Ice::PluginManager 接口,經過它,你能夠訪問通訊器的插件集。
² 對象工廠:爲了實例化從已知基類派生的類,通訊器維護有一組對象工廠,可以替Ice run time 對類進行實例化。用戶定義的每個接口/類都隱式地繼承同一個基類Ice:Object.
² 缺省定位器:定位器用於把客戶請求的對象標識解析爲代理對象。
² 對象適配器:對象適配器用於分派接收到的請求,把每一個請求轉發到正確的Servant。每一個對象適配器能夠綁定多個ICE對象,不一樣通訊器的對象適配器和對象徹底是相互獨立的。
一個通訊器含有一個或更多對象適配器。對象適配器處在Ice run time和服務器之間的界線上,負有這樣一些責任:
² 它把Ice對象映射到到來請求的servant,並把請求分派給每一個servant中的應用代碼(也就是說,對象適配器實現了一個向上調用接口,把Icerun time 與服務器中的應用代碼鏈接在一塊兒)。
² 它協助進行生命週期操做,使得Ice對象和servant在建立和銷燬時不會出現競爭情況。
² 它提供一個或更多傳輸端點。客戶經過這些端點訪問適配器所提供的Ice對象。每一個對象適配器都有一個或更多servant,對Ice 對象進行體現;同時還有一個或更多傳輸端點。若是對象適配器擁有的傳輸端點不止一個,全部向該適配器做了註冊的servant 能夠在任何一個端點上響應到來的請求。換句話說,若是對象適配器有多個傳輸端點,這些端點表明的是通往同一組對象的不一樣通訊路徑(例如,經過不一樣的傳輸機制)。
² 每一個對象適配器都只屬於一個通訊器(但一個通訊器能夠有多個對象適配器)。
Ice run time 的一個有用的特性是位置透明性:客戶無需知道Ice 對象的實現的位置;對某個對象的調用會被自動引導到正確的目標,無論這個對象的實現是在本地地址空間中,在同一臺機器上的另外一個地址空間中,仍是在一臺遠 地機器上的另外一個地址空間中。位置透明性十分重要,由於有了它,咱們可以改變對象實現的位置,而不會破壞客戶程序,同時,經過使用IceGrid,像域名和端口號這樣的信息能夠放在應用的外部,不用出如今串化代理中。
ICE平臺支持客戶端異步調用(AMI)和服務端異步分派編程(AMD)。
異步方法調用(AMI) 這個術語描述的是客戶端的異步編程模型支持。若是你使用AMI 發出遠地調用,在Ice run time 等待答覆的同時,發出調用的線程不會阻塞。相反,發出調用的線程能夠繼續進行各類活動,當答覆最終到達時, Ice run time 會通知應用。通知是經過回調發給應用提供的編程語言對象的。
要使用異步方法調用,只須要給對應的類或者方法前面加上元數據[「ami」],示例以下:
interface Hello
{
[」ami」] string sayHello(int num);
};
經過Slice編譯器生成頭文件時,會生成對應的方法同步調用方法和異步調用方法,用戶在客戶端能夠自行選擇使用同步調用仍是異步調用,很是方便。編譯後對於每個異步方法都會生成相應的異步回調類和異步調用代理方法,類的命名方式 AMI_類名_方法名,上述的slice經編譯後生成的類爲 AMI_Hello_sayHello,同時這個類提供了兩個方法:
void ice_response(<params>);
代表操做已成功完成。各個參數表明的是操做的返回值及out 參數。若是操做的有一個非void 返回類型, ice_response 方法的第一個參數就是操做的返回值。操做的全部out 參數都按照聲明時的次序,跟在返回值的後面。
void ice_exception(const Ice::Exception &);
代表拋出了本地或用戶異常。
客戶端只須要從AMI_Hello_sayHello 類繼續並實現上述兩個方法就能夠了。示例代碼以下:
class AMI_Hello_sayHelloI : public AMI_Hello_sayHello
{
void ice_response(const ::std::string& strMsg)
{
printf("%s\n",strMsg.c_str());
}
void ice_exception(const Ice::Exception& exception)
{
printf("error\n");
}
};
生成的異步調用代理方法命名爲方法名_async,上述示例生成的方法爲 sayHello_async.而後客戶端的程序異步調用以下:
hello->sayHello_async(AMI_Hello_sayHelloPtr,<params>);
而後只需在ice_response 方法處理返回結果。
也能夠調用同步方法:
hello->sayHello(<params>);
異步方法分派(AMD) 是AMI 的服務器端等價物,在使用AMD 時,服務器能夠接收一個請求,而後掛起其處理,以儘快釋放分派線程。當處理恢復、結果已得出時,服務器要使用Ice runtime 提供的回調對象,顯式地發送響應。
用實際的術語說, AMD 操做一般會把請求數據(也就是,回調對象和操做參數)放入隊列 ,供應用的某個線程(或線程池)隨後處理用。這樣,服務器就使分派線程的使用率降到了最低限度,可以高效地支持大量併發客戶。
要使用異步方法調用,只須要給對應的類或者方法前面加上元數據[「amd」],示例以下:
interface Hello
{
[」amd」] string sayHello(int num);
};
和異步方法調用不一樣的是,異步分派是服務端的,在服務端會生成相應的異步分派類和異步分派方法。服務端的分派器在接收到客戶請求時把請求分派給sayHello_async(const Demo::AMD_Hello_sayHelloPtr&, int, const Ice::Current&).而後用戶能夠在該方法內把任務參數信息放入隊列,在隊列中循環檢查任務並處理,處理完成後調用 AMD_Hello_sayHelloPtr 類的 ice_response 方法返回給客戶端。
IceStorm是一個高效的用於ICE應用的發佈/訂閱服務,IceStorm有幾個比較重要的概念:
² 消息:IceStorm的消息和普通的消息隊列中間件中描述的消息有點區別,IceStorm 的消息是強類型的,由對某個Slice 操做的調用表明:操做名標識消息的類型,操做參數定義消息內容。要發佈消息,能夠按普通的方式調用某個IceStorm 代理上的操做。與此相似,訂閱者會像收到常規的向上調用(upcall)同樣收到消息。因此IceStorm 的消息遞送使用的是「推」模式
² 主題:應用要經過訂閱某個主題(topic)來代表本身有興趣接收某些消息。IceStorm 服務器可以支持任意數量的主題,這些主題是動態建立的,經過惟一的名字來區分。每一個主題均可以有多個發佈者和訂閱者。
² 持久模式:IceStorm 擁有一個數據庫,裏面維護的是關於其主題和連接的信息。可是,經過IceStorm 發送的消息不會被持久地存儲,而是會在遞送給主題目前的訂閱者集以後,立刻被丟棄。若是在把消息遞送給某個訂閱者的過程當中發生錯誤, IceStorm 不會爲該訂閱者進行消息排隊。
² 訂閱者出錯:由於IceStorm 消息是採用單向語義遞送的, IceStorm 只能檢測到鏈接或超時錯誤。若是在把消息遞送給訂閱者的過程當中, IceStorm 遇到這樣的錯誤,該訂閱者就會馬上被解除與該消息對應的主題的訂閱。固然用戶在使用過程當中也能夠經過設定QOS參數來改善這個問題,好比重試次數(retryCount),可是對於ObjectNotExistException 或者 NotRegisteredException之類的硬錯誤,Ice運行時不會重試,而是仍然直接解除訂閱關係。
IceStorm支持兩個主要的QOS參數reliability 和 retryCount,reliability的取值分別爲ordered 和空值,取ordered時,發佈者發佈的消息會保證按順序遞送給訂閱者。
從IceStorm提供的功能來看,對於不須要進行消息持久存儲轉發的應用來講很適合,可是因爲在訂閱者出錯後當即解除訂閱關係,不是由訂閱者主動解除,這個在應用中須要特別注意是否符合實際應用。
IceStorm被實現爲IceBox服務,因此在部署IceStorm應用時須要啓動IceBox服務。
IceBox 用於動態加載用戶服務並對他們進行集中管理,能夠經過iceboxadmin管理工具對IceBox中的服務進行遠程管理,經過IceBox用戶服務能夠被開發成能夠動態加載的動態庫組件.
使用IceBox的服務組件須要繼承IceBox::Service類,並實現start()、stop()方法,並在實現類中提供服務進入點函數,通常爲create()函數,在這函數中建立服務實現類的對象並返回。例如:
extern "C"
{
ICE_DECLSPEC_EXPORT IceBox::Service*
create(Ice::CommunicatorPtr communicator)
{
return new HelloServiceI;
}
}
對於Ice3.3.0以上版本,iceboxadmin提供了啓動、中止服務及中止IceBox 服務器的命令管理工具和應用程序接口,管理工具命令以下:
iceboxadmin [options] [command...]
Commands:
start SERVICE Start a service.
stop SERVICE Stop a service.
shutdown Shutdown the server.
IceGrid用於支持分佈式網絡服務應用,一個IceGrid域由一個註冊表(Registry)和任何數目的節點(Node)構成。註冊表(Registry)和節點(Node)一塊兒合做管理一些信息以及包含一些應用(Application)的服務進程。每項應用(Application)被指定在特定節點上的服務。這個註冊表(Registry)持久記錄了這些信息,而節點(Node)負責啓動和監測其指定的服務器進程。對於一個典型的配置,一個節點(Node)運行在一臺計算機(稱之爲Ice服務器主機)。註冊表(Registry)並不消耗不少處理器時間,因此它經常是和一個節點(Node)運行在同一臺計算機上的,註冊表(Registry)還能夠和一個節點(Node)能夠運行在同一進程中.若是須要容錯,註冊表(Registry)還能夠用主從式的設計支持複製(Replication)。
註冊表(Registry)的主要責任,是解決做爲Ice定位服務的間接代理問題,當客戶端第一次嘗試使用一種間接代理,客戶端Ice run time首先鏈接註冊表(registry),註冊表將間接代理的符號信息轉化爲直接代理的endpoint,而後客戶端和直接代理創建一個鏈接。經過適配器複製,同名適配器能夠分佈在多個節點上,間接代理能夠映射到多個節點上的直接代理,在運行時由註冊表服務根據負載均衡自動選擇一個直接代理給客戶端。
使用間接代理時,客戶端能夠用如下方式直接獲取服務對象代理:
MyProxy=theObject@theAdapter // 對象@適配器
更簡單一點的話能夠用如下方式
MyProxy=theObject // 對象
在部署IceGrid分佈式服務時,須要啓動註冊表服務(icegridregistry),並配置註冊表服務地址端口、通訊協議和註冊信息保存的目錄地址(ICE的註冊信息保存爲BerkeleyDB的數據庫文件):
IceGrid.Registry.Client.Endpoints=tcp -p 4061
IceGrid.Registry.Data=/opt/ripper/registry
在服務器節點中和客戶端都須要配置註冊表服務的地址端口和通訊協議:
Ice.Default.Locator=IceGrid/Locator:tcp -h 172.0.0.1 -p 4061
而後分別啓動註冊表服務(icegridregistry)和節點服務(icegridnode).
ICE提供了部署工具icegridadmin, 這個icegridadmin工具也須要定義Ice.Default.Locator屬性.
接下須要編寫應用部署文件,應用部署文件以XML方式保存。如下爲支持適配器複製的應用配置文件,使用了服務模板:
<icegrid>
<application name="Ripper">
<replica-group id="EncoderAdapters"> //定義適配器複製組
<object identity="EncoderFactory" //identity將在客戶端中使用。
type="::Ripper::MP3EncoderFactory"/>
</replica-group>
<server-template id="EncoderServerTemplate"> //定義服務器模板
<parameter name="index"/>
<parameter name="exepath"
default="/opt/ripper/bin/server"/>
<server id="EncoderServer${index}"
exe="${exepath}"
activation="on-demand">
<adapter name="EncoderAdapter"
replica-group="EncoderAdapters"
endpoints="tcp"/>
</server>
</server-template>
<node name="Node1">
<server-instance template="EncoderServerTemplate"
index="1"/>
</node>
<node name="Node2">
<server-instance template="EncoderServerTemplate"
index="2"/>
</node>
</application>
</icegrid>
而後在客戶端能夠用如下方式獲取對象代理:
Ice::ObjectPrx obj = communicator->stringToProxy("EncoderFactory");
ICE平臺內嵌負載均衡功能,對於分佈大多個節點上的應用服務提供多種負載均衡方案,只須要經過XML配置文件便可完成負載均衡配置。配置項包括Type (負載均衡類型)、Sampling interval(負載信息收集間隙)、Number of replicas(返回給客戶端的適配器個數)。
負載均衡類型有如下4種方式:
² Random (隨機方式):註冊中心隨機選擇一個適配器給客戶端,不檢查適配器的負載。
² Adaptive(適配方式):註冊中心從全部適配器中選擇一個負載最輕的適配器給客戶端,Sampling interval參數只有在該類型的負載均衡中有效,這個參數指定節點按期向註冊中心報告本地系統負載信息(system load information);
² Round Robin(最近最少使用):註冊中心從對應的適配器組中選擇一個最近最少使用的適配器給客戶。
² Ordered(順序方式):註冊中心根據適配器的優先級,從高到低順序選擇一個適配器給客戶端。
配置示例:
<replica-group id="EncoderAdapters">
<load-balancing type="adaptive"/> //配置爲適配方式
<object identity="EncoderFactory"
type="::Ripper::MP3EncoderFactory"/>
</replica-group>
前兩節中描述的是屬於用戶應用的分佈部署,分佈式部署一個很重要的支撐是ICE的註冊中心,全部客戶端都向註冊中心查詢服務代理的真實端點,從而創建通訊鏈接,在這裏註冊中心又成了一個單點服務,爲了不註冊中心成爲應用的瓶頸,提升系統的可靠性,ICE3.3.0以上版本提供了註冊中心集羣功能。
ICE註冊中心集羣經過主從式的註冊中心複製來實現,一個集羣中有一個主註冊中心,若干個副註冊中心,主從的區別經過IceGrid.Registry.ReplicaName屬性配置來實現,主註冊中心的名稱爲 Master,其餘的名字能夠任意取。啓動時先啓動主註冊中心,再啓動其餘註冊中心,經過主註冊中心更新的信息都將同步給副註冊中心,各副註冊中心之間不通訊。若是主註冊中心失效,須要從其餘副註冊中心提撥一個成爲主註冊中心,可是從3.3版本的說明文檔中來看,若是須要把某個副註冊中心提撥成爲主註冊中心須要從新啓動相應進程並修改IceGrid.Registry.ReplicaName 屬性值爲Master,或者刪除該屬性,默認狀況下該屬性值爲Master.
使用集羣方式時,在客戶端配置時把全部的主從註冊中心地址端口所有填到Ice.Default.Locator,例如:
Ice.Default.Locator=IceGrid/Locator:default -p 12000:default -p 12001
在應用節點也把全部的註冊中心地址端口綁定,這樣應用的更新會同時通知全部的註冊中心。
ICE平臺能夠經過簡單的配置來支持SSL應用,配置過程以下:
² 首先須要經過修改配置文件來啓用SSL插件,C++服務端的配置方法爲:Ice.Plugin.IceSSL=IceSSL:createIceSSL
只須要把IceSSL動態庫放到LD_LIBRARY_PATH包含的路徑下便可。
² 而後修改適配器的監聽選項:
MyAdapter.Endpoints=tcp -p 8000:ssl -p 8001:udp -p 8000 //表示該適配器在三種協議端口上同時監聽。
ICE還提供了多種配置屬性來知足實際應用,例以下例所示:
Ice.Plugin.IceSSL=IceSSL:createIceSSL
IceSSL.DefaultDir=/opt/certs //默認證書目錄
IceSSL.CertFile=pubkey.pem //證書文件
IceSSL.KeyFile=privkey.pem //私鑰文件
IceSSL.CertAuthFile=ca.pem //信任的根證書文件
IceSSL.Password=password //私鑰文件查看密碼
ICE提供的持久化方案能夠支持普通用戶數據(鍵/值對)的持久化存儲和服務對象實例的持久化管理,普通用戶數據的持久化存儲使用比較簡單,服務對象實例的管理相對複雜一點,暫時不關注。
ICE的持久存儲介質爲BerkeleyDB,對普通數據的持久化在C++實現中採用Map的方式進行操做,用戶須要用Slice定義須要存儲的數據,並用slice2freeze 生成相應的Map操做類,而後對數據的操做就可使用Map容器函數來進行。示例以下:
首先生成須要存儲的數據類型:
slice2freeze --dict StringIntMap,string,int StringIntMap
代碼使用:
Ice::CommunicatorPtr communicator =
Ice::initialize(argc, argv);
// Create a Freeze database connection.
Freeze::ConnectionPtr connection = Freeze::createConnection(communicator, "db"); //鏈接到數據庫文件。
// Instantiate the map.
StringIntMap map(connection, "simple");//建立表。
// Clear the map.
map.clear();
Ice::Int i;
StringIntMap::iterator p;
// Populate the map.
for (i = 0; i < 26; i++)
{
std::string key(1, 'a' + i);
map.insert(make_pair(key, i));
}
// Iterate over the map and change the values.
for (p = map.begin(); p != map.end(); ++p)
p.set(p->second + 1);
// Find and erase the last element.
p = map.find("z");
assert(p != map.end());
map.erase(p);
// Clean up.
connection->close();
communicator->destroy();
IceFreeze還容許使用結構體和類對象做爲值進行存儲,可是隻有public的成員變量會被存儲,其餘成員變量不會被存儲。
對於較高版本的ICE,還容許對值創建索引,若是值爲結構體或者類對象,那麼還容許以結構體/對象變量做爲索引,經過slice2freeze編譯後會生成對應的索引查詢函數。例如定義了以下須要存儲的數據結構:
module Demo
{
struct Struct1
{
long l;
};
class Class1
{
string s;
};
};
而後執行如下命令生成映射表,同時生成索引,以class1的成員變量s爲索引。
Slice2freeze
--dict Demo::IndexedStruct1Class1Map,Demo::Struct1,Demo::Class1 --dict-index Demo::IndexedStruct1Class1Map,s,case-sensitive BenchTypes Test.ice
編譯後代碼中會自動生成findByS(string &),在程序中能夠按如下方式直接調用:
IndexedStruct1Struct2Map& m=...;
IndexedStruct1Struct2Map::iterator p = m.findByS(os.str());
ICE平臺提供的功能比較多,除了文檔中羅列的部分,還支持程序包分發(IcePath2)、防火牆穿透(Glacier),鑑於目前的項目應用暫時不對這兩部分做介紹。
從ICE官方提供的Demo和本身編寫的測試程序在Iinux(opensuse)運行良好,對於適用於AIX的3.1.1版本在AIX上運行異步編程的測試也很順利,可是目前還未對ICE平臺的應用作性能測試。
從文檔的介紹,ICE平臺支持同步/異步、訂閱/發佈、分佈式部署、內部持久化存儲,支持接口描述語言到各類面向對象開發語言的映射,能夠知足ESB系統開發的技術需求,可是也有必定的風險,ICE3.1之後的版本對AIX、HP-UX操做系統不保證徹底支持,對3.1之後各個升級版本須要進行編譯並運行測試。