前言
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的一个简单应用,后续文章会介绍它的高级部署模式。