当前位置 博文首页 > whao2world:ZeroC Ice系列--基于IceGrid的部署方案
前一篇文章介绍了IceGrid的简单应用。这篇文章来介绍一下它的高端玩法—如何将模板,复制组,知名对象应用于部署方案及其作用。
之前介绍了xml格式的配置文件通过各种描述符如node,server,adaptor等,描述应用部署信息。当服务部署复杂度增加时,书写这些描述符是件头疼的事。
模板(Template)能大大简化描述符的书写;同时参数化使得模板的使用更加灵活。我们可以为描述符server和service定义模板,这个章节主要介绍
Server Template,在IceBox部署章节再介绍Service Template。
我们继续使用之前的例子做一下扩展,将PrinterServer部署到两个node节点。
<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}”方式获取。
模板中其他描述符的定义与之前没有差别。server-instance描述符是用来实例化一个server的,它属性template,index都是作为参数,template指定了
模板(PrinterServerTemplate),index作为参数传递给了模板。
通过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