Ice系列--基于IceGrid的部署方案
前言
前一篇文章介绍了IceGrid的简单应用。这篇文章来介绍一下它的高端玩法—如何将模板,复制组,知名对象应用于部署方案及其作用。
基于模板的部署方案
之前介绍了xml格式的配置文件通过各种描述符如node,server,adaptor等,描述应用部署信息。当服务部署复杂度增加时,书写这些描述符是件头疼的事。
模板(Template)能大大简化描述符的书写;同时参数化使得模板的使用更加灵活。我们可以为描述符server和service定义模板,这个章节主要介绍
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>
上述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)就是为了解决这个问题,同时也是实现对客户端透明的负载均衡的基础。
复制组(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。
知名对象(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="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,这个非常有用的组件不集成到IceGrid部署方案中,岂不浪费。
部署IceBox Server
一个简单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工具部署,结果如下:

Service Templates
跟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的定义依然,不够简洁。
升级版Service Templates
在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>
负载均衡的类型
- 随机(Random)
不考虑节点系统负载,随机返回服务连接端点
- 自适应(Adaptive)
根据系统负载,返回负载最少的节点的服务连接端点
- 循环(Round Robin)
不考虑节点系统负载,返回最近最少被使用的对象适配器的服务连接端点。
- 排序(Order)
根据对象适配器配置的优先级,来排序返回服务连接端点
最终部署方案
一个客户端调用简单,动态配置,服务冗余,负载均衡的方案--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
Ice系列--基于IceGrid的部署方案的更多相关文章
- Ice系列--傻瓜式服务开发IceBox
前言 相信大家在没有接触过框架之前,都自己或多或少的开发过一些应用服务.每个应用服务除了业务配置还有很多环境配置,资源配置等,这些跟部署相关的配置.服务跟配置文件是一种静态绑定的方式,更新配置还需要重 ...
- 基于webhook方案的Git自动部署方案
之前已经用Git实现了自己博客的提交自动部署,并自动提交到GitHub和coding以备不时之需.平时项目代码都托管在Coding或者GitHub上,也已经用上了coding提供的webhook功能, ...
- Ice系列--强大如我IceGrid
前言 IceGrid是一个提供服务定位和服务激活的组件,但它的功能远不止于此.从它的命名可以看出它的设计理念-网格计算(grid computing).网格计算被定义为由一系列关联的廉价计算机组成的计 ...
- 重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关
重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关 引言 重复造轮子系列是自己平时的一些总结.有的轮子依赖社区提供的轮子为基础,这里把使用过程的一些觉得有意思的做个分享.有些思路或者方法在 ...
- spring boot 自动部署方案
现在主流的自动部署方案大都是基于Docker的了,但传统的自动部署方案比较适合中小型公司,下面的方案就是比较传统的自动部署方案. 1.为什么需要自动部署 基于微服务的架构,自动部署显得非常重要.因为每 ...
- iOS动态部署方案
转载: iOS动态部署方案 前言 这里讨论的动态部署方案,就是指通过不发版的方式,将新的内容.新的业务流程部署进已发布的App.因为苹果的审核周期比较长,而且苹果的限制比较多,业界在这里也没有特别多的 ...
- 【Xamarin开发 Android 系列 1】环境部署搭建
原文:[Xamarin开发 Android 系列 1]环境部署搭建 开篇自然先扯一段,近几年移动互联网如果熊猫零食一样,蔓延迅速.楼主身为一个微软忠实的粉丝,无奈,老爹不给力.Silverlight开 ...
- JS组件系列——基于Bootstrap Ace模板的菜单Tab页效果优化
前言:之前发表过一篇 JS组件系列——基于Bootstrap Ace模板的菜单和Tab页效果分享(你值得拥有) ,收到很多园友的反馈,当然也包括很多诟病,因为上篇只是将功能实现了,很多细节都没有处理 ...
- Kvm虚拟化的一种打包及部署方案(采用tar包,lvm方式)
Kvm虚拟化的一种打包部署方案(采用tar包,lvm方式) –-–-–-2016年终总结 一 毕业之后跟师兄学到的第一块主要内容,理解花了不少时间.期间经历了shell的入门.linux基础知识入门. ...
随机推荐
- 1-解决java Scanner出现 java.util.NoSuchElementException
起因:在函数中新建scanner对象,然后多次调用此方法出现上述异常 原因:Scanner(system.in)在Scanner中接受的是键盘 输入,当调用close()方法时 Scanner的关闭会 ...
- 服务器安装ESXI6.7
1 从官网下载ESXI镜像文件到本地 https://my.vmware.com/web/vmware/details?downloadGroup=ESXI670&productId=7 ...
- Jquery Ajax如何添加header参数
转自网络 1 $.ajax({ 2 type: "POST", 3 url: "http://192.168.0.88/action.cgi?ActionID=WEB_R ...
- Weblogic命令执行漏洞(CVE-2018-2628)复现
一.漏洞环境搭建 CVE-2018-2628影响的软件版本为: Weblogic 10.3.6.0 Weblogic 12.1.3.0 Weblogic 12.2.1.2 Weblogic 12.2. ...
- Spring循环依赖解决方式源码解析
1. 什么是循环依赖? 循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环.比如A依赖于B,B依赖于A我们直接上代码 先创建一个类ServiceA依赖于Service ...
- [leetcode]Q133Clone Graph
克隆图记住:一个map一个queue,照葫芦画瓢BFS 找到一个节点就画一个对应的新的,用map对应,然后添加邻居,记录邻居到queue public UndirectedGraphNode clon ...
- lua脚本简介
Lua [1] 是一个小巧的脚本语言.它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个由Roberto Ier ...
- WEBSERVICE之CXF框架开发webservice
之前学习了使用jdk开发webservice服务,现在开始学习使用框架(cxf)开发webservice. 1.准备工作 A.使用cxf开发webservice服务,需要用到apache-cxf-3. ...
- windows和Linux的文件路径
(1)windows的文件路径格式"E:\Python\workplace\codes"单反斜杠的方式,但是在很多编程语言中会不认识"\"字符,可能会把它识别成 ...
- Modbus仿真器 Modbus Poll 和Modbus Slave详细图文教程
Modbus Poll 是Witte Software公司开发的的Modbus主机仿真器,用于测试和调试Modbus从设备.软件支持ModbusRTU.ASCII.TCP/IP协议.支持多设备监控,可 ...