開發分布式或較大型的軟件時,必不可少的要進行系統間通信,目前比較常用的框架有Http RestFul,Thrift,gRPC等等,今天分享的ZeroC Ice也是其中一員。
ZeroC公司出品的Ice(Internet Communication Engine)框架專注於RPC通信,經過了10多年的發展,已經非常的成熟,它的主要優點是高性能,跨語言,跨平台,面向對象,開源等等,可以查看其官方網站(https://zeroc.com/products/ice) 了解更多。這里簡單介紹一下它的架構和使用方法。
架構和基本概念
先上一張從ZeroC官方網站上截的圖:

從圖中可以看出,使用Ice的程序分為客戶端和服務器端,而兩端的代碼都由以下三部分組成:
- 應用程序代碼,這是由使用者編寫的部分
- 自動生成的代碼,由Ice提供的工具自動生成的,為client端生成的叫proxy,為server端生成的叫skeleton
- Ice庫代碼,這部分是Ice框架的核心部分,各個平台都有對應的庫
從整體來看,Ice框架的結構還是很清晰的,下面介紹幾個關鍵的概念:
- Slice(Specification Language for Ice):用於定義client和server通信接口的領域特定語言(DSL),在 https://doc.zeroc.com/ice/3.7/the-slice-language 中有關於它的詳細信息
- Communicator:Ice運行時的入口點,它所關聯的資源包括:線程池,運行時的配置屬性,對象工廠,Logger(用於處理Ice運行時產生的log消息),Plug-in manager,Object Adapters等
- Ice Object:是一個抽象的概念,它具有類型,標識等信息,有點像C#或Java里類的概念
- Servants:上面說到Ice Object是抽象概念,而這里的Servant就是提供具體操作的實體,它負責響應client請求的,一個Servant可以對應一個或者多個Ice Object
- Proxy:代表Ice Object,client使用proxy來和server交互
- Ice Object Adapter:存在服務端的communicator中,負責Ice運行時和Server端應用代碼的交互,它會和一個或多個Endpoint綁定並接收client端的請求,然后把請求轉給對應的Servant從而執行應用代碼
使用方法
在了解了Ice的結構和基本概念之后,讓我們動手寫個demo看看具體怎么使用吧。為了體現Ice的跨語言和跨平台功能,我們這里用Java實現server端,用C#實現client端。程序的主要功能:client可以通過向sever發送A股的股票代碼來獲得其對應公司的詳細信息, 下面我們一起看看具體的步驟。(注:這里的公司信息都是dummy的)
-
下載並安裝Ice
從 https://zeroc.com/download/Ice/3.7/Ice-3.7.4.msi 下載Ice 3.7.4並安裝,筆者把它安裝在了D:\Program Files下面,打開D:\Program FilesZeroC\Ice-3.7.4\bin文件夾,我們可以看到有名為slice2java.exe, slice2cs.exe的程序,這些就是用來自動生成Java和C#代碼的,我們在下面會用到。除了Java和C#的代碼生成器,還有許多其他語言的,如:cpp, php等等

-
定義client和service的交互接口:這里我們定義兩個class:CompanyInfo和AStockService:
module com
{
module astock
{
class CompanyInfo
{
int id;
string name;
string addr;
}
}
}
module com
{
module astock
{
interface AStockService
{
CompanyInfo GetCompanyInfo(int id);
}
}
}
-
使用1中的工具把2中定義的類生成C#和Java對應的代碼
打開cmd,執行下圖中的命令,通過執行slice2java和slice2cs程序,我生成了如下圖所示的代碼,其中C# code只有一個文件,而Java code有四個文件

-
生成Code的簡單介紹
C#雖然只有一個文件,但是里面包含了三個接口,三個類,一個委托,如下圖:

- AStockServiceOperations_接口:包含了我們定義的操作即GetCompanyInfo
- AStockService接口:我們定義的接口,它繼承自AStockServiceOperations_
- AStockServicePrx接口:客戶端代理接口
- CompanyInfo類: 用於傳遞數據的DTO
- AStockServiceDisp_類: 服務端的dispatch抽象類,即上文中說到的skeleton,它實現了我們定義的接口AStockService
- AStockServicePrxHelper: 客戶端的代理類,它實現了AStockServicePrx接口
Java的code生成了兩個類,兩個接口
- AStockService接口:我們定義的接口,也是服務端的skeleton
- AStockServicePrx接口:客戶端代理接口
- CompanyInfo類: 用於傳遞數據的DTO
- _AStockServicePrxI類:客戶端代理類
-
編寫服務端代碼
用熟悉的IDE新建一個工程,筆者使用的Intelij Idea,把上面生成的Java代碼加到項目中,並添加Ice的Maven依賴,如下圖所示,當然也可以手動下載ice jar包,並手動添加。

下面創建一個類AStockServiceServer,實現AStockService接口,其GetCompanyInfo方法返回一個dummy CompanyInfo對象,如下所示
public class AStockServiceServer implements AStockService { @Override public CompanyInfo GetCompanyInfo(int id, Current current) { CompanyInfo info = new CompanyInfo(); info.id = 1234; info.name = "中國平安"; info.addr = "深圳"; return info; } }創建包含main的class AStockServiceServerMain,如下所示:
public class AStockServiceServerMain {
public static void main(String[] args) {
try (Communicator communicator = Util.initialize()) {//創建communicator
ObjectAdapter oa = communicator.createObjectAdapterWithEndpoints("AStockServiceAdapter", "default -p 10000");//創建一個Adatper,Id是AStockServiceAdapter,綁定到10000端口
AStockServiceServer servant = new AStockServiceServer();//我們的服務
oa.add(servant, Util.stringToIdentity("AStockService"));//把我們創建的服務加到上面創建的adapter里
oa.activate();//激活adapter
System.out.println("AStock Service Server is running");//輸出啟動log
communicator.waitForShutdown();//等待結束
}
}
}
-
編寫客戶端代碼
用VS新建一個控制台程序,並把上面生成的C#代碼加入項目中,然后添加Ice的nuget包,如下圖所示:

在main函數中編寫如下代碼:
class Program
{
static void Main(string[] args)
{
using (var communicator = Util.initialize(ref args))//創建Communicator對象
{
ObjectPrx basePrx = communicator.stringToProxy("AStockService:default -p 10000");//創建客戶端基類代理
AStockServicePrx aStockServicePrx = AStockServicePrxHelper.checkedCast(basePrx);//把基類代理轉換為子類代理
var companyInfo = aStockServicePrx.GetCompanyInfo(1000);//調用GetCompanyInfo方法
Console.WriteLine($"id:{companyInfo.id} name:{companyInfo.name} addr:{companyInfo.addr}");//輸出返回結果
}
}
}
- 聯調
先運行Java服務端,然后再啟動C#程序可以得到如下結果,可以看到Client端成功的獲取到了CompanyInfo對象。
總結
本文介紹了ZeroC Ice的概念並用一個demo詳細說明了具體使用方法,完整代碼請參考 https://github.com/DerekLoveCC/Writings/tree/master/Article/zerocIce/code ,期望對讀者能夠有所幫助


