前言
IceGrid是一個提供服務定位和服務激活的組件,但它的功能遠不止於此。從它的命名可以看出它的設計理念—網格計算(grid computing)。網格計算被定義為由一系列關聯的廉價計算機組成的計算網絡。將寫好的應用運行於網格計算中的主機上,只是應用整個生命周期中一部分工作。雖然Ice為應用的各個組成部分的之間通信提供了基礎設施(RPC通信框架),但是我們還會面臨很多挑戰:
- 如何安裝升級網格計算中的應用
- 如何跟蹤網格計算中運行的服務
- 如何分發負載到所有的主機
- 如何將服務從一個主機遷移到另外一個主機
- 如何快速添加一個主機到網格中
今天看到這些分布式應用也面臨問題時,是不是立馬會想到容器,K8S,這個當下比較主流的解決方案。要知道Ice在十幾年前就實現了一個成熟解決方案--IceGrid。以下是它的特性:
- 定位服務(Location service)
- 按需激活服務器(On-demand server activation)
- 應用分發(Application distribution)
- 復制和負載均衡(Replication and load balancing)
- 會話和資源分配(Sessions and resource allocation)
- 自動故障恢復(Automatic failover)
- 動態查詢(Dynamic queries)
- 管理(Adminstration)
- 部署(Deployment)
IceGrid使得開發人員擺脫了這些低級的任務,加快了應用構建,簡化了應用部署管理。
原理框架
IceGrid主要有兩個重要的組成:注冊中心(registry)和節點(node)。注冊中心主要是維護服務路由信息,以及一個客戶端請求過來后,去啟動指定服務器;
或者根據各個主機的負載情況,返回路由信息。節點可以看做一個服務器集合,管理着這些服務器。如下圖,一個簡單的IceGrid應用:
從客戶端角度,registry就是一個名字服務,將間接代理標識串,如 SimplePrinter@PrinterAdapter,轉化成服務器連接端點。
以一次客戶端調用為例:
1.客戶端先向注冊中心發起定位請求,通過間接代理標識串獲取到對象適配器(PrinterAdapter)的服務地址端口
2.通過服務地址端口與Server(PrinterServer)建立連接
3.調用checkedCast,檢驗對象(SimplePrinter)是否存在
4.如果服務對象存在,則發起遠程調用過程。不存在則拋出異常
環境部署
軟件包安裝參考官方文檔。本文章運行的環境是:ubuntu 16.04,ice 3.7.2
registry和node的啟動腳本和配置文件見:https://github.com/GodMonking/ice-demo/tree/main/deploy
啟動registry
配置文件registry.cfg:

# Registry properties IceGrid.InstanceName=MKIceGrid Ice.Default.Locator=MKIceGrid/Locator:tcp -p 4061 IceGrid.Registry.ReplicaName=Master #IceGrid.Registry.Client.Endpoints=tcp -p 4061 IceGrid.Registry.Client.Endpoints=tcp -p 4061 IceGrid.Registry.Server.Endpoints=tcp IceGrid.Registry.Internal.Endpoints=tcp IceGrid.Registry.AdminPermissionsVerifier=MKIceGrid/NullPermissionsVerifier IceGrid.Registry.AdminPermissionsVerifier=MKIceGrid/NullPermissionsVerifier IceGrid.Registry.SSLPermissionsVerifier=MKIceGrid/NullSSLPermissionsVerifier IceGrid.Registry.AdminSSLPermissionsVerifier=MKIceGrid/NullSSLPermissionsVerifier IceGrid.Registry.Discovery.Interface=127.0.0.1 IceGrid.Registry.LMDB.MapSize=10 #注冊中心數據保存路徑 IceGrid.Registry.LMDB.Path=/data/ice-demo/deploy/registry IceGrid.Registry.DynamicRegistration=1 IceGrid.Registry.Trace.Node=1 IceGrid.Registry.Trace.Replica=1 # # Dummy username and password for icegridadmin. # IceGridAdmin.Username=foo IceGridAdmin.Password=bar
執行命令:
/usr/bin/icegridregistry --Ice.Config=./config/registry.cfg --daemon
啟動node
啟動兩個node節點
配置文件node.cfg:

# Node properties #默認定位器,即注冊中心的服務地址端口 Ice.Default.Locator=MKIceGrid/Locator:tcp -p 4061 IceGrid.Node.Endpoints=tcp IceGrid.Node.Name=node1 #數據存儲路徑 IceGrid.Node.Data=/data/ice-demo/deploy/node1 #日志輸出路徑 IceGrid.Node.Output=/tmp/node1 IceGrid.Node.Trace.Replica=2 IceGrid.Node.Trace.Activator=3 IceGrid.Node.Trace.Adapter=3 IceGrid.Node.Trace.Server=3
拷貝node.cfg :cp node.cfg node2.cfg
修改配置node2.cfg結果如下:

# Node properties Ice.Default.Locator=MKIceGrid/Locator:tcp -p 4061 IceGrid.Node.Endpoints=tcp IceGrid.Node.Name=node2 IceGrid.Node.Data=/data/ice-demo/deploy/node2 IceGrid.Node.Output=/tmp/node2 IceGrid.Node.Trace.Replica=2 IceGrid.Node.Trace.Activator=3 IceGrid.Node.Trace.Adapter=3 IceGrid.Node.Trace.Server=3
執行命令:
/usr/bin/icegridnode --Ice.Config=./config/node.cfg –daemon
/usr/bin/icegridnode --Ice.Config=./config/node2.cfg –daemon
通過命令行工具查看是否部署成功:
/usr/bin/icegridadmin -H localhost -P 4061
由於注冊中心沒有開啟鑒權,所以這里賬號密碼任意輸入。
也可以通過IceGrid GUI工具查看。Windows下安裝IceGrid GUI,打開IceGrid GUI,新建一個連接,登錄注冊中心
服務部署
部署配置文件,客戶端程序和服務端程序代碼見:
https://github.com/GodMonking/ice-demo/tree/main/IceGrid/Printer
簡單應用開發
服務端部分代碼:

int main(int argc, char* argv[]) { try { if (argc < 2) { cerr << "not input config file" << endl; return 1; } Ice::CommunicatorHolder ich(argc, argv); auto communicator = ich.communicator(); auto properties = communicator->getProperties(); cout << "adapter: " << properties->getProperty("SimplePrinterAdapter.AdapterId") << endl; //注意這里的對象適配器名字要與下文的部署配置中的adaptor的屬性name保持一致 auto adapter = ich->createObjectAdapter("SimplePrinterAdapter"); auto servant = make_shared<PrinterI>(); adapter->add(servant, Ice::stringToIdentity("SimplePrinter")); adapter->activate(); cout << "activate complete..." << endl; ich->waitForShutdown(); } catch(const std::exception& e) { cerr << e.what() << endl; return 1; }return 0; }
客戶端程序代碼:

#include <Ice/Ice.h> #include <Printer.h> #include <stdexcept> using namespace std; using namespace Demo; int main(int argc, char* argv[]) { try { //Ice::CommunicatorHolder ich(argc, argv, "config.client"); Ice::CommunicatorHolder ich(argc, argv); auto base = ich->stringToProxy("SimplePrinter@SimplePrinterAdapter"); auto printer = Ice::checkedCast<PrinterPrx>(base); if(!printer) { throw std::runtime_error("Invalid proxy"); } printer->printString("Hello World!"); } catch(const std::exception& e) { cerr << e.what() << endl; return 1; } return 0; }
"SimplePrinter@SimplePrinterAdapter"表示一個間接代理的標識字符串,
SimplePrinterAdapter表示對象適配器ID,對應下文部署配置中adaptor的屬性id值。
客戶端配置文件config.client:
Ice.Default.Locator=MKIceGrid/Locator:tcp -p 4061
上述配置表示一個定位器,定位器即注冊中心提供服務地址端口。
部署配置文件
應用程序部署配置文件主要是向注冊中心描述幾個重要元素:Nodes,Servers,Object adaptors,Objects。如下圖配置文件:
<icegrid> <application name="Ripper"> <node name="node1"> <server id="PrinterServer" exe="/data/ice-demo/IceGrid/Printer/server" activation="on-demand"> <adapter name="SimplePrinterAdapter" id="SimplePrinterAdapter" endpoints="tcp"/> <property name="Ice.Trace.Network" value="1"/> <property name="Ice.PrintStackTraces" value="1"/> <property name="Ice.Admin.Endpoints" value="tcp" /> </server> </node> </application> </icegrid>
上圖中,元素node描述了server歸屬於node1節點,即表明PrinterServer部署在node1所在主機上。元素server的屬性exe則描述可執行文件的路徑,
屬性activation描述服務器激活方式,“on-demand”表示按需激活,即有請求達到時才被激活。元素adaptor描述了對象適配器(Object adaptor)的信息。
通過IceGrid GUI部署應用
點擊如下箭頭所指按鈕
打開配置文件
加載之后生成表單,如下;點擊箭頭所指按鈕,將配置保存到注冊中心
可以看到node1下面已經有服務PrinterServer
驗證服務
執行客戶端程序
./client --Ice.Config=config.client
可以看到服務被激活
選擇服務右擊鼠標,查看標准輸出信息:
結尾
IceGrid不僅提供了定位服務,負載均衡等功能,同時還簡化了服務發布和部署。本文介紹了IceGrid的一個簡單應用,后續文章會介紹它的高級部署模式。