分散式RPC框架ZeroC Ice簡介
開發分散式或較大型的軟體時,必不可少的要進行系統間通訊,目前比較常用的框架有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 ,期望對讀者能夠有所幫助