1. 程式人生 > 實用技巧 >分散式RPC框架ZeroC Ice簡介

分散式RPC框架ZeroC Ice簡介

開發分散式或較大型的軟體時,必不可少的要進行系統間通訊,目前比較常用的框架有Http RestFul,Thrift,gRPC等等,今天分享的ZeroC Ice也是其中一員。

ZeroC公司出品的Ice(Internet Communication Engine)框架專注於RPC通訊,經過了10多年的發展,已經非常的成熟,它的主要優點是高效能,跨語言,跨平臺,面向物件,開源等等,可以檢視其官方網站(https://zeroc.com/products/ice) 瞭解更多。這裡簡單介紹一下它的架構和使用方法。

架構和基本概念

先上一張從ZeroC官方網站上截的圖:

從圖中可以看出,使用Ice的程式分為客戶端和伺服器端,而兩端的程式碼都由以下三部分組成:

  1. 應用程式程式碼,這是由使用者編寫的部分
  2. 自動生成的程式碼,由Ice提供的工具自動生成的,為client端生成的叫proxy,為server端生成的叫skeleton
  3. 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的)

  1. 下載並安裝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等等

  2. 定義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. 使用1中的工具把2中定義的類生成C#和Java對應的程式碼

    開啟cmd,執行下圖中的命令,通過執行slice2java和slice2cs程式,我生成了如下圖所示的程式碼,其中C# code只有一個檔案,而Java code有四個檔案

  2. 生成Code的簡單介紹

    C#雖然只有一個檔案,但是裡面包含了三個介面,三個類,一個委託,如下圖:

    • AStockServiceOperations_介面:包含了我們定義的操作即GetCompanyInfo
    • AStockService介面:我們定義的介面,它繼承自AStockServiceOperations_
    • AStockServicePrx介面:客戶端代理介面
    • CompanyInfo類: 用於傳遞資料的DTO
    • AStockServiceDisp_類: 服務端的dispatch抽象類,即上文中說到的skeleton,它實現了我們定義的介面AStockService
    • AStockServicePrxHelper: 客戶端的代理類,它實現了AStockServicePrx介面

    Java的code生成了兩個類,兩個介面

    • AStockService介面:我們定義的介面,也是服務端的skeleton
    • AStockServicePrx介面:客戶端代理介面
    • CompanyInfo類: 用於傳遞資料的DTO
    • _AStockServicePrxI類:客戶端代理類
  3. 編寫服務端程式碼

    用熟悉的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();//等待結束
        }
    }
}
  1. 編寫客戶端程式碼

    用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}");//輸出返回結果
            }
        }
    }

  1. 聯調

先執行Java服務端,然後再啟動C#程式可以得到如下結果,可以看到Client端成功的獲取到了CompanyInfo物件。

總結

本文介紹了ZeroC Ice的概念並用一個demo詳細說明了具體使用方法,完整程式碼請參考 https://github.com/DerekLoveCC/Writings/tree/master/Article/zerocIce/code ,期望對讀者能夠有所幫助