当前位置 博文首页 > whao2world:ZeroC Ice系列--基于IceGrid的部署方案

    whao2world:ZeroC Ice系列--基于IceGrid的部署方案

    作者:whao2world 时间:2021-01-30 18:59

    前言

    前一篇文章介绍了IceGrid的简单应用。这篇文章来介绍一下它的高端玩法—如何将模板,复制组,知名对象应用于部署方案及其作用。

     

    基于模板的部署方案

    之前介绍了xml格式的配置文件通过各种描述符如node,server,adaptor等,描述应用部署信息。当服务部署复杂度增加时,书写这些描述符是件头疼的事。

    模板(Template)能大大简化描述符的书写;同时参数化使得模板的使用更加灵活。我们可以为描述符serverservice定义模板,这个章节主要介绍

    Server Template,在IceBox部署章节再介绍Service Template。

    服务器模板(Server Templates)

    我们继续使用之前的例子做一下扩展,将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>
    View Code

    这个例子很适合使用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>
    View Code

    上述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)
    ...
    View Code

    测试调用成功:

    这里就有一个问题了,同一个服务,两个实例分别部署在两个节点。客户端调用服务还需要间接代理指定不同对象适配器ID才能请求到不同实例。

    是不是感觉这个有点low,不应该像DNS那样一个域名解析请求,返回多个IP地址吗?Ice作为RPC框架的佼佼者,这个小问题怎么可能没有考虑到。

    之前提到过的对象适配器复制和复制组(Replica Group)就是为了解决这个问题,同时也是实现对客户端透明的负载均衡的基础。

    复制组(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>
    View Code

    这个版本的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)
    ...
    View Code

    测试一下,执行n次调用之后

     

     

     调用都有请求到了PrinterServer3,PrinterServer4。

    知名对象(Well-Known Object)

    上一个小节留下了一个坑,现在来填。可能有人觉得这种形式“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
    
    下一篇:没有了