轉自:http://blog.csdn.net/zhu2695/article/details/51494664java
一、ICE是什麼?
ICE是ZEROC的開源通訊協議產品,它的全稱是:The Internet Communications Engine,翻譯爲中文是互聯網通訊引擎,是一個面向對象的中間件,使咱們可以以最小的代價構建分佈式應用程序。ICE使咱們專一於應用邏輯的開發,它來處理全部底層的網絡接口編程,這樣咱們就不用去考慮這樣的細節:打開網絡鏈接、網絡數據傳輸的序列化與反序列化、鏈接失敗的嘗試次數等。
二、爲何會有ICE?
ICE是分佈式應用的一種比較好的解決方案,雖然如今也有一些比較流行的分佈式應用解決方案,如微軟的.NET(以及原來的DCOM)、CORBA及WEB SERVICE等,可是這些面向對象的中間件都存在一些不足:
.NET是微軟產品,只面向WINDOWS系統,而實際的狀況是在當前的網絡環境下,不一樣的計算機會運行不一樣的系統,如LINUX上面就不可能使用.NET;
CORBA雖然在統一標準方面作了不少的工做,可是不一樣的供應商實現之間仍是缺少互操做性,而且目前尚未一家供應商能夠針對全部的異種環境提供全部的實現支持,且CORBA的實現比較複雜,學習及實施的成本都會比較高;
WEB SERVICE最要命的缺點就是他的性能問題,對於要求比較高的行業是不多會考慮WEB SERVICE的。
ICE的產生就是源於.NET、CORBA及WEB SERVICE這些中間件的不足,它能夠支持不一樣的系統,如WINDOWS、LINUX等,也能夠支持在多種開發語言上使用,如C++、C、JAVA、RUBY、PYTHON、VB等,服務端能夠是上面提到的任何一種語言實現的,客戶端也能夠根據本身的實際狀況選擇不一樣的語言實現,如服務端採用C語言實現,而客戶端採用JAVA語言實現,底層的通信邏輯經過ICE的封裝實現,咱們只須要關注業務邏輯。
三、ICE是如何工做的?
Ice 是一種面向對象的中間件平臺,這意味着 Ice爲構建面向對象的客戶-服務器應用提供了工具、API 和庫支持。要與Ice持有的對象進行通訊,客戶端必須持有這個對象的代理(與CORBA的引用是相同的意思),這裏的代理指的是這個對象的實例,ICE在運行時會定位到這個對象,而後尋找或激活它,再把In參數傳給遠程對象,再經過Out參數獲取返回結果。
這裏提到的代理又分爲直接代理和間接代理,直接代理其內部保存有某個對象的標識,以及它的服務器的運行地址;間接代理指的是其內部保存有某個對象的標識,以及對象適配器名(object adapter name),間接代理沒有包含尋址信息,爲了正確地定位服務器,客戶端在運行時會使用代理內部的對象適配器名,將其傳給某個定位器服務,好比IcePack服務,而後,定位器會把適配器名看成關鍵字,在含有服務器地址的表中進行查找,把當前的服務器地址返回給客戶,客戶端 run time如今知道了怎樣聯繫服務器,就會像日常同樣分派 (dispatch)客戶請求。
ICE能夠保證在任何的網絡環境或者操做系統下,成功的調用只有一次,它在運行時會盡力的定位到遠程服務器,在鏈接失敗的狀況下會作嘗試性重複性鏈接,確實連不上的狀況會給用戶以提示。
客戶端在調用服務端的方法時,能夠採起同步或異步的方式實現,同步調用就至關於調用本身本地的方法同樣,其它行爲會被阻塞;異步調用是很是有用的調用方式,如服務端須要準備的數據來自於其它異步接口,這個時候客戶端就不須要等待,待服務端數據準備充份後,以消息的方式通知客戶端,服務端就能夠去幹其它的事情了,而客戶端也能夠到服務端獲取數據了。
四、ICE調用模式
ICE採用的網絡協議有TCP、UDP以及SSL三 種,不一樣於WebService,ICE在調用模式上有好幾種選擇方案,而且每種方案正對不一樣的網絡協議的特性作了相應的選擇。
Oneway(單向調用):客戶端只需將調用註冊到本地傳輸緩衝區(Local Transport Buffers)後就當即返回,不會等待調用結果的返回,不對調用結果負責。
Twoway(雙向調用):最通用的模式,同步方法調用模式,只能用TCP或SSL協議。
Datagram(數據報):相似於Oneway調用,不一樣的是 Datagram調用只能採用UDP協議並且只能調用無返回值和無輸出參數的方法。
BatchOneway(批量單向調用):先將調用存 在調用緩衝區裏面,到達必定限額後自動批量發送全部請求(也可手動刷除緩衝區)。
BatchDatagram(批量數據報):與上相似。
不一樣的調用模式其實對應着不動的業務,對於大部分的有返回值的或須要實時響應的方法,咱們可能都採用Twoway方式調用,對於一些無需返回值或 者不依賴返回值的業務,咱們能夠用Oneway或者BatchOneway方式,例如消息通知;剩下的Datagram和BatchDatagram方式 通常用在無返回值且不作可靠性檢查的業務上,例如日誌。
五、客戶端與服務端的結構
這個圖示顯示了使用ICE作爲中間件平臺,客戶端及服務端的應用都是由應用代碼及ICE的庫代碼混合組成的。
客戶應用及服務器應用分別對應用的是客戶端與服務端。
代理是根據SLICE定義的ice文件實現,它提供了一個向下調用的接口,提供了數據的序列化與反序化。
ICE的核心部份,提供了客戶端與服務端的網絡鏈接等核心通訊功能,以及其它的網絡通訊功能的實現及可能的問題的處理,讓咱們在編寫應用代碼的時候沒必要要去關注這一塊,而專一於應用功能的實現。
6. 要使用ICE,必須先安裝ICE,安裝及配置參考以下:
WINDOWS:http://blog.csdn.net/fenglibing/archive/2011/04/28/6368665.aspx
LINUX(BDB的安裝還有問題,沒法使用SLICE2JAVA):http://blog.csdn.net/fenglibing/archive/2011/04/27/6367559.aspx
這個示例是JAVA示例,是從ICE的幫助文檔中摘出來的,是一個輸出Hello World的測試程序,採用的ICE版本是3.1.1。1)、準備一個ice文件並命名爲:Printer.ice,其內容爲:
module Demo {
interface Printer {
void printString(string s);
};
};
2)、轉到命令行,在Printer.ice文件保存目錄執行命令:
slice2java Printer.ice
會在目錄下面生成一個Demo 文件夾,裏面會生成一些JAVA文件,以下圖示:
3)、這些文件的類圖結構以下:
這裏對生成的一些文件作些解釋,分兩兩部份,服務端類文件及客戶端類文件:
• <interface-name>.java
這個源文件聲明在ICE文件中定的接口名稱的Java接口,如這裏是Printer。
• _<interface-name>Operations.java
_<interface-name>OperationsNC.java
這是兩個定義操做的接口文件,每一個接口文件中定義了一個操做實現,定義的操做與Slice接口中定義的操做相一致,只是在_<interface-name>Operations.java中定義的方法多了一個參數「Ice.Current __current」(注:Current對象的定義,請參見3.1.1版本文檔中的31.6 The Ice::Current Object說明),這個參數的做用是能夠容許咱們訪問 「正在執行的請求」和 「服務器中的操做的實現」等信息,也就是咱們的請求需求須要其它請求的支持時或者要獲取其它請求的執行結果時,咱們能夠調用這個方法,這兩個接口文件都會被接口文件_<interface-name>.java繼承。
• _<interface-name>Disp.java 這個文件包含的是服務器端骨架類的定義,所用接口定義都要繼承這個東西,這裏的接口指供客戶端調用的接口。
• <interface-name>PrxHolder.java 代理定義holder 類,是對應Out參數使用的。通常參數都是值傳遞,這個類的做用是使參數經過引用傳遞。ICE框架應用了不少反射機制,這個類是改變遠程參數的一個映射。
• _<interface-name>Del.java
• _<interface-name>DelD.java
• _<interface-name>DelM.java
不用關心上面的這些文件,這些文件包含的是供Java 映射內部使用的代碼;它們包含的功能與應用程序無關。
• <interface-name>Prx.java 這個是代理接口。例如PrinterPrx,在客戶的地址空間中, PrinterPrx 的實例是「遠地的服務器中的Printer接口的實例」的「本地大使」。與服務器端對象有關的全部細節,好比其地址、所用協議、對象標識,都封裝在該實例中。
注意, PrinterPrx 繼承自Ice.ObjectPrx。這反映了這樣一個事實:全部的Ice 接口都隱式地繼承自Ice::Object。
說的更明白些,就是這個類的方法調用都是遠程服務端的調用,執行printString()方法的具體實現是在遠程服務端執行的。
• <interface-name>PrxHelper.java 這個是接口的代理定義助手類,就是幫你得到代理類的。常常用的就兩個方法checkedCast 和 uncheckedCast 。這兩個方法實現的都是向下轉換。
注意, checkedCast 會聯繫服務器。這是必要的,由於只有服務器狀況中的代理實現確切地知道某個對象的類型。因此, checkedCast 可能會拋出ConnectTimeoutException 或ObjectNotExistException(這也解釋了爲什麼須要助手類:ICE在運行時必須聯繫服務器,因此咱們不能使用Java 的向下轉換)。
與此相反, uncheckedCast 不會聯繫服務器,而是會無條件地返回具備所請求的類型的代理 。可是,若是你要使用uncheckedCast,你必須肯定這個代理真的支持你想要轉換到的類型;而若是你弄錯了,你極可能會在調用代理上的操做時,引起運行時異常。對於這樣的類型失配,最後可能會引起OperationNotExistException,但也有可能引起其餘異常,好比整編異常。並且,若是對象碰巧有一個同名的操做,但參數類型不一樣,則有可能根本不產生異常,你最後就會把調用發送給類型錯誤的對象;這個對象可能會作出很是糟糕的事情。
4)、創建一個ECLIPSE工程,將生成的文件拷貝到src目錄下,並在classpath中導入Ice.jar。
5) 、創建三個測試JAVA文件,Server.java、PrinterI.java及Client.java:
PrinterI.java是對服務端實現骨架類_PrinterDisp的實現,返回時將PrinterI.java對象返回給客戶端,這裏實現的功能是直接輸出傳入的String參數:
public class PrinterI extends Demo._PrinterDisp {
public void printString(String s, Ice.Current current) {
System.out.println(s);
}
}
Server.java是服務端服務代理,用於接收客戶端的請求操做:
public class Server {
public static void main(String[] args) {
int status = 0;
Ice.Communicator ic = null;
try {
//初使化鏈接,args能夠傳一些初使化參數,如鏈接超時時間,初使化客戶鏈接池的數量等
ic = Ice.Util.initialize(args);
//建立名爲SimplePrinterAdapter的適配器,並要求適配器使用缺省的協議(TCP/IP偵聽端口爲10000的請求)
Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000");
//實例化一個PrinterI對象,爲Printer接口建立一個服務對象
Ice.Object object = new PrinterI();
//將服務單元增長到適配器中,並給服務對象指定名稱爲SimplePrinter,該名稱用於惟一肯定一個服務單元
adapter.add(object, Ice.Util.stringToIdentity("SimplePrinter"));
//激活適配器,這樣作的好處是能夠等到全部資源就位後再觸發
adapter.activate();
//讓服務在退出以前,一直持續對請求的監聽
ic.waitForShutdown();
} catch (Ice.LocalException e) {
e.printStackTrace();
status = 1;
} catch (Exception e) {
System.err.println(e.getMessage());
status = 1;
}
if (ic != null) {
// Clean up
//
try {
ic.destroy();
} catch (Exception e) {
System.err.println(e.getMessage());
status = 1;
}
}
System.exit(status);
}
}
Client.java是客戶端代碼,用於向服務端發起請求,並操做返回的代理對象:
public class Client {
public static void main(String[] args) {
int status = 0;
Ice.Communicator ic = null;
try {
//初使化
ic = Ice.Util.initialize(args);
//傳入遠程服務單元的名稱、網絡協議、IP及端口,獲取Printer的遠程代理,這裏使用的stringToProxy方式
Ice.ObjectPrx base = ic.stringToProxy("SimplePrinter:default -p 10000");
//經過checkedCast向下轉換,獲取Printer接口的遠程,並同時檢測根據傳入的名稱獲取的服務單元是否Printer的代理接口,若是不是則返回null對象
Demo.PrinterPrx printer = Demo.PrinterPrxHelper.checkedCast(base);
if (printer == null) throw new Error("Invalid proxy");
//把Hello World傳給服務端,讓服務端打印出來,由於這個方法最終會在服務端上執行
printer.printString("Hello World!");
} catch (Ice.LocalException e) {
e.printStackTrace();
status = 1;
} catch (Exception e) {
System.err.println(e.getMessage());
status = 1;
}
if (ic != null) {
// Clean up
//
try {
ic.destroy();
} catch (Exception e) {
System.err.println(e.getMessage());
status = 1;
}
}
System.exit(status);
}
}
6)、運行客戶端和服務端
運行服務端:java Server
運行客戶端:java Client
看看效果吧。
七、ICE的性能和效率
ICE的性能是比較好的,由於他自己的傳輸機制都是基於二進制,網上有人曾經作過性能測試,評價比較好,我本人尚未作性能測試,目前的判斷只是基於網絡數據,請先看下面的文章:
高性能計算-ICE 性能測試
ICE與CORBA比較的優點
八、ICE的優勢
支持同步和異步的消息傳遞;
支持多個接口;
機器無關性,客戶及服務器與底層的機器架構屏蔽開來。對於應用代碼而言,像字節序和填充這樣的問題都隱藏了起來;
語言無關性,客戶和服務器能夠分別部署,所用語言也能夠不一樣;
實現無關性,客戶不知道服務器是怎樣實現其對象的。這意味着,在客戶部署以後,服務器的實現能夠改變;
操做系統無關性,Ice API 徹底是可移植的,因此一樣的源碼可以在 Windows和 UNIX
上編譯和運行;
線程支持,Ice run time 徹底是線程化的,其 API 是線程安全的,做爲應用開發者,(除了在訪問共享數據時進行同步)無需爲開發線程化的高性能客戶和服務器付出額外努力。
傳輸機制無關性,Ice 目前採用了TCP/IP 和 UDP做爲傳輸協議。客戶和服務器代碼都不須要了解底層的傳輸機制;
位置和服務器透明性,Ice run time 會負責定位對象,並管理底層的傳輸機制,好比打開和關閉鏈接;
安全性,經過 SSL強加密,可使客戶和服務器徹底安全地進行通訊,這樣,應用可使用不安全的網絡安全地進行通訊,你可使用 Glacier穿過防火牆,實現安全的請求轉發,而且徹底支持回調;
內建的持久機制,使用 Freeze,建立持久的對象實現變成了一件很容易的事情,Ice提供了對高性能數據庫 Berkeley DB[18] 的內建支持;
開放源碼。
後記
這裏只是簡單的對ICE進行介紹,還有不少東西沒有提到,如ICE的語法規則、ICE的版本控制(Facet)、持久化 (Feeze)、服務裝箱管理 (ICEBox)、文件分發(ICEPatch2)、發佈/訂閱 服務(ICEStorm)、網絡拓撲負載解決方案--終極武器(ICEGrid)、提供使用安全傳輸入協議SSL的插件(IceSSL)、輕量級的ICE應用防火牆其解決方案(Galcier2),這些留待你們後面去學習了。數據庫