前一篇文章介紹了IceGrid的簡單應用。這篇文章來介紹一下它的高端玩法—如何將模板,複製組,知名對象應用於部署方案及其做用。node
以前介紹了xml格式的配置文件經過各類描述符如node,server,adaptor等,描述應用部署信息。當服務部署複雜度增長時,書寫這些描述符是件頭疼的事。git
模板(Template)能大大簡化描述符的書寫;同時參數化使得模板的使用更加靈活。咱們能夠爲描述符server和service定義模板,這個章節主要介紹github
Server Template,在IceBox部署章節再介紹Service Template。服務器
咱們繼續使用以前的例子作一下擴展,將PrinterServer部署到兩個node節點。app
<icegrid> <application name="Ripper"> <node name="node1"> <server id="PrinterServer1" exe="/data/ice-demo/IceGrid/Printer/server" activation="on-demand"> <adapter name="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> <node name="node2"> <server id="PrinterServer2" exe="/data/ice-demo/IceGrid/Printer/server" activation="on-demand"> <adapter name="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>
這個例子很適合使用Server Template,使用模板以後:負載均衡
<icegrid> <application name="Ripper"> <server-template id="PrinterServerTemplate"> <parameter name="index"/> <server id="PrinterServer${index}" exe="/data/ice-demo/IceGrid/Printer/server" activation="on-demand"> <adapter name="SimplePrinterAdapter" endpoints="tcp"/> <property name="Ice.Trace.Network" value="1"/> <property name="Ice.PrintStackTraces" value="1"/> <property name="Ice.Admin.Endpoints" value="tcp" /> </server> </server-template> <node name="node1"> <server-instance template="PrinterServerTemplate" index="1"/> </node> <node name="node2"> <server-instance template="PrinterServerTemplate" index="2"/> </node> </application> </icegrid>
上述xml文件中,先來看一下最上面的模板定義,server-template做爲服務器模板的描述符描述了一個模板的信息,屬性id做爲區別其餘模板的標識。框架
模板參數的定義,經過parameter描述符來定義,name指定參數名稱,default指定其默認值。這裏「index」就是模板參數名,其值經過」${index}」方式獲取。dom
模板中其餘描述符的定義與以前沒有差異。server-instance描述符是用來實例化一個server的,它屬性template,index都是做爲參數,template指定了tcp
模板(PrinterServerTemplate),index做爲參數傳遞給了模板。ide
經過IceGrid GUI工具部署服務,以下:
使用以前Printer客戶端程序測試一下:
結果拋出異常,對象適配器id「SimplePrinterAdapter」沒有註冊。
因爲上面xml只是定義adaptor的name,沒有定義id。因此有可能對象適配器的Id可能不是「SimplePrinterAdapter」。
經過界面工具查看到PrinterServer1的SimplePrinterAdapter的適配器ID爲」PrinterServer1.SimplePrinterAdapter」。
PrinterServer2的SimplePrinterAdapter的適配器ID爲」PrinterServer2.SimplePrinterAdapter」
從這裏能夠看出,若是用戶不定義描述符adaptor的id則系統會生成一個${server}.${name}的對象適配器ID。
修改客戶端程序:
... try { //Ice::CommunicatorHolder ich(argc, argv, "config.client"); Ice::CommunicatorHolder ich(argc, argv); auto base = ich->stringToProxy("SimplePrinter@PrinterServer1.SimplePrinterAdapter"); auto printer = Ice::checkedCast<PrinterPrx>(base); if(!printer) { throw std::runtime_error("Invalid proxy"); } printer->printString("Hello World!"); } catch(const std::exception& e) ...
測試調用成功:
這裏就有一個問題了,同一個服務,兩個實例分別部署在兩個節點。客戶端調用服務還須要間接代理指定不一樣對象適配器ID才能請求到不一樣實例。
是否是感受這個有點low,不該該像DNS那樣一個域名解析請求,返回多個IP地址嗎?Ice做爲RPC框架的佼佼者,這個小問題怎麼可能沒有考慮到。
以前提到過的對象適配器複製和複製組(Replica Group)就是爲了解決這個問題,同時也是實現對客戶端透明的負載均衡的基礎。
以前文章作過簡單介紹,對象適配器複製簡單地講就是多個服務實例,複製組就是一個對象適配器集合,這個集合映射到多個服務實例的地址端口;
這個映射關係是由註冊中心來維護的。因此客戶端能夠將複製組看作一個虛擬對象適配器,不須要關心它與實際服務實例的映射。
使用複製組來部署Printer服務:
<icegrid> <application name="Printer"> <replica-group id="PrinterAdapters"> <load-balancing type="random" n-replicas="0"/> <!-- 此處還能夠添加知名對象的定義(Well-Know Object) --> </replica-group> <server-template id="PrinterServerTemplate"> <parameter name="index"/> <server id="PrinterServer${index}" exe="/data/ice-demo/IceGrid/Printer/server" activation="on-demand"> <adapter name="SimplePrinterAdapter" endpoints="tcp" replica-group="PrinterAdapters"/> <property name="Ice.Trace.Network" value="1"/> <property name="Ice.PrintStackTraces" value="1"/> <property name="Ice.Admin.Endpoints" value="tcp" /> </server> </server-template> <node name="node1"> <server-instance template="PrinterServerTemplate" index="3"/> </node> <node name="node2"> <server-instance template="PrinterServerTemplate" index="4"/> </node> </application> </icegrid>
這個版本的xml文件相比以前,添加了複製組的定義和在描述adaptor添加了屬性replica-group,這表示將對象適配器加入了複製組。
經過IceGrid GUI工具部署服務,以下:
修改客戶端程序,將對象適配器ID—「PrinterServer1.SimplePrinterAdapter」替換成複製組ID—「PrinterAdapters」,代碼以下:
... try { //Ice::CommunicatorHolder ich(argc, argv, "config.client"); Ice::CommunicatorHolder ich(argc, argv); auto base = ich->stringToProxy("SimplePrinter@PrinterAdapters"); auto printer = Ice::checkedCast<PrinterPrx>(base); if(!printer) { throw std::runtime_error("Invalid proxy"); } printer->printString("Hello World!"); } catch(const std::exception& e) ...
測試一下,執行n次調用以後
調用都有請求到了PrinterServer3,PrinterServer4。
上一個小節留下了一個坑,如今來填。可能有人以爲這種形式「SimplePrinter@PrinterAdapters」的間接代理不夠簡潔,接下來介紹一種更簡潔的--知名代理。
它只由一個對象ID組成,而這樣的對象被定義爲知名對象。註冊中心不只存儲了知名對象ID與代理的關係,同時還有對象類型。這此對象類型通常是加上
完整的命名空間的Slice Type,如::Demo::Printer;它的主要做用仍是用來進行服務查詢。
在上一節的例子基礎上,加入知名對象,進行部署:
<icegrid> <application name="Printer"> <replica-group id="PrinterAdapters"> <load-balancing type="random" n-replicas="0"/> <!-- 此處還能夠添加知名對象的定義(Well-Know Object) --> <object identity="SimplePrinter" type="::Demo::Hello" /> </replica-group> <server-template id="PrinterServerTemplate"> <parameter name="index"/> <server id="PrinterServer${index}" exe="/data/ice-demo/IceGrid/Printer/server" activation="on-demand"> <adapter name="SimplePrinterAdapter" endpoints="tcp" replica-group="PrinterAdapters"/> <property name="Ice.Trace.Network" value="1"/> <property name="Ice.PrintStackTraces" value="1"/> <property name="Ice.Admin.Endpoints" value="tcp" /> </server> </server-template> <node name="node1"> <server-instance template="PrinterServerTemplate" index="3"/> </node> <node name="node2"> <server-instance template="PrinterServerTemplate" index="4"/> </node> </application> </icegrid>
以前文章介紹過IceBox,這個很是有用的組件不集成到IceGrid部署方案中,豈不浪費。
一個簡單IceBox Server部署配置文件以下:
<icegrid> <application name="IceBoxDemo"> <node name="node1"> <icebox id="IceBoxServer" exe="/usr/bin/icebox++11" activation="always" pwd="/opt/ice/Hello"> <env>LD_LIBRARY_PATH=/opt/ice-demo/IceGrid/lib:$LD_LIBRARY_PATH</env> <property name="Ice.Admin.Endpoints" value="tcp" /> <service name="Hello" entry="HelloI:create"> <adapter name="${service}" endpoints="tcp"/> <property name="Ice.Trace.Network" value="1" /> <property name="Ice.Trace.ThreadPool" value="1"/> <property name="Ice.PrintStackTraces" value="1"/> </service> </icebox> </node> </application> </icegrid>
能夠看到這裏出現了兩個新的描述符icebox和service,跟部署server很類似,不一樣點在於service。service也包含對象適配器,配置屬性這些描述信息。
一個icebox下能夠定義多個service,service的順序決定了被加載的順序。
經過IceGrid GUI工具部署,結果以下:
跟server template很相似,service template是用來描述service的。廢話很少說,先來看一個簡單實例:
<icegrid> <application name="IceBoxDemov2"> <service-template id="ServiceTemplate"> <parameter name="name" /> <service name="${name}" entry="HelloI:create"> <adapter name="${service}" endpoints="tcp"/> <property name="${service}.Identity" value="${server}-${service}"/> <property name="Ice.Trace.Network" value="1" /> <property name="Ice.PrintStackTraces" value="1"/> </service> </service-template> <node name="node1"> <icebox id="IceBoxServerv2" exe="/usr/bin/icebox++11" activation="on-demand" pwd="/opt/ice/Hello"> <env>LD_LIBRARY_PATH=/data/ice-demo/IceGrid/lib:$LD_LIBRARY_PATH</env> <property name="Ice.Admin.Endpoints" value="tcp" /> <service-instance template="ServiceTemplate" name="Liming"/> <service-instance template="ServiceTemplate" name="Jane"/> <service-instance template="ServiceTemplate" name="Machel"/> </icebox> </node> </application> </icegrid>
此icebox server(IceBoxServerv2)下經過service模板定義三個模板實例。能夠看到icebox server的定義依然,不夠簡潔。
在Server Template中實例化Service Template:
<icegrid> <application name="IceBoxDemov3"> <service-template id="ServiceTemplate"> <parameter name="name" /> <service name="${name}" entry="HelloI:create"> <adapter name="${service}" endpoints="tcp"/> <property name="${service}.Identity" value="${server}-${service}"/> <property name="Ice.Trace.Network" value="1" /> <property name="Ice.PrintStackTraces" value="1"/> </service> </service-template> <server-template id="ServerTemplate"> <parameter name="icebox_id" /> <parameter name="name" /> <icebox id="${icebox_id}" exe="/usr/bin/icebox++11" activation="on-demand"> <env>LD_LIBRARY_PATH=/data/ice-demo/IceGrid/lib:$LD_LIBRARY_PATH</env> <property name="Ice.Admin.Endpoints" value="tcp" /> <service-instance template="ServiceTemplate" name="${name}" /> </icebox> </server-template> <node name="node1"> <server-instance template="ServerTemplate" icebox_id="IceBoxServerv3-1" name="Babala" /> </node> <node name="node2"> <server-instance template="ServerTemplate" icebox_id="IceBoxServerv3-2" name="Maria" /> </node> </application> </icegrid>
以前講過複製組的另外一個做用就是負載均衡;ICE的負載均衡是在註冊中心實現的。IceGrid的節點會向註冊中心上報主機系統負載信息,註冊中心會根據複製組(replica groups)配置的負載均衡策略來決定如何處理定位請求。
IceGrid的負載均衡能力能夠幫助客戶端獲取一個包含服務鏈接端點集合的代理,與服務者創建一個鏈接。一旦客戶端創建了一個鏈接,在沒有與註冊中心進一步交互協商的狀況下,全部後續請求都是發送到同一個Server。這就是說若是客戶端想動態的獲取路由信息,須要週期性主動發起定位請求更新服務鏈接端點。
複製組能夠包含負載均衡描述符(load-balancing),來決定如何根據系統負載來返回服務路由信息。負載均衡描述符包含幾個屬性來描述負載均衡的策略:
支持的幾種負載均衡類型
以必定的間隔採樣各個節點負載信息
配置數值爲N。若未配置,默認配置爲1。若N>1,則返回對象適配器鏈接端點數量最多爲N;N=0,則返回全部鏈接端點。
格式以下:
<replica-group id="ReplicatedAdapter"> <load-balancing type="adaptive" load-sample="5" n-replicas="2"/> </replica-group>
不考慮節點系統負載,隨機返回服務鏈接端點
根據系統負載,返回負載最少的節點的服務鏈接端點
不考慮節點系統負載,返回最近最少被使用的對象適配器的服務鏈接端點。
根據對象適配器配置的優先級,來排序返回服務鏈接端點
一個客戶端調用簡單,動態配置,服務冗餘,負載均衡的方案--IceBox + Server Templates + Service Templates + 複製組。
<icegrid> <application name="IceBoxDemov3"> <service-template id="ServiceTemplate"> <parameter name="name" /> <service name="${name}" entry="HelloI:create"> <adapter name="${service}" endpoints="tcp" replica-group="HelloGroup" /> <property name="${service}.Identity" value="${server}-${service}"/> <property name="Ice.Trace.Network" value="1" /> <property name="Ice.PrintStackTraces" value="1"/> </service> </service-template> <server-template id="ServerTemplate"> <parameter name="icebox_id" /> <icebox id="${icebox_id}" exe="/usr/bin/icebox++11" activation="on-demand"> <env>LD_LIBRARY_PATH=/opt/ice/Hello-v3/lib:$LD_LIBRARY_PATH</env> <property name="Ice.Admin.Endpoints" value="tcp" /> <service-instance template="ServiceTemplate" name="Liming" /> </icebox> </server-template> <replica-group id="HelloGroup"> <load-balancing type="adaptive" load-sample="5" n-replicas="1" /> <object identity="HelloImp" type="::Demo::Hello" /> </replica-group> <node name="node1"> <server-instance template="ServerTemplate" icebox_id="IceBoxServerv4-1" /> </node> <node name="node2"> <server-instance template="ServerTemplate" icebox_id="IceBoxServerv4-2" /> </node> </application>
IceGrid的各類部署方案基本介紹完了,完整用例源碼見文章最後的github鏈接。
ICE這個框架細節的東西比較多,只有實踐才能慢慢掌握它,但願這篇文章能幫助想入坑ICE的同窗打開大門。
注:源碼鏈接 https://github.com/GodMonking/ice-demo/tree/main/IceGrid