前言

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应用:

Ice系列--强大如我IceGrid-LMLPHP

从客户端角度,registry就是一个名字服务,将间接代理标识串,如 SimplePrinter@PrinterAdapter转化成服务器连接端点。

以一次客户端调用为例:

1.客户端先向注册中心发起定位请求,通过间接代理标识串获取到对象适配器(PrinterAdapter)的服务地址端口

2.通过服务地址端口与Server(PrinterServer)建立连接

3.调用checkedCast,检验对象(SimplePrinter)是否存在

4.如果服务对象存在,则发起远程调用过程。不存在则抛出异常

Ice系列--强大如我IceGrid-LMLPHP

环境部署

软件包安装参考官方文档。本文章运行的环境是:ubuntu 16.04,ice 3.7.2

registry和node的启动脚本和配置文件见:https://github.com/GodMonking/ice-demo/tree/main/deploy

启动registry

配置文件registry.cfg:

Ice系列--强大如我IceGrid-LMLPHPIce系列--强大如我IceGrid-LMLPHP
# 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
View Code

执行命令:

/usr/bin/icegridregistry --Ice.Config=./config/registry.cfg --daemon

启动node

启动两个node节点

配置文件node.cfg:

Ice系列--强大如我IceGrid-LMLPHPIce系列--强大如我IceGrid-LMLPHP
# 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
View Code

拷贝node.cfg :cp node.cfg node2.cfg

修改配置node2.cfg结果如下:

Ice系列--强大如我IceGrid-LMLPHPIce系列--强大如我IceGrid-LMLPHP
# 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
View Code

执行命令:

/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

Ice系列--强大如我IceGrid-LMLPHP

由于注册中心没有开启鉴权,所以这里账号密码任意输入。

也可以通过IceGrid GUI工具查看。Windows下安装IceGrid GUI,打开IceGrid GUI,新建一个连接,登录注册中心

Ice系列--强大如我IceGrid-LMLPHP

 Ice系列--强大如我IceGrid-LMLPHP

服务部署

部署配置文件,客户端程序和服务端程序代码见:

https://github.com/GodMonking/ice-demo/tree/main/IceGrid/Printer

简单应用开发

服务端部分代码:

Ice系列--强大如我IceGrid-LMLPHPIce系列--强大如我IceGrid-LMLPHP
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;
}
View Code

客户端程序代码:

Ice系列--强大如我IceGrid-LMLPHPIce系列--强大如我IceGrid-LMLPHP
#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;
}
View Code

"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部署应用

点击如下箭头所指按钮

Ice系列--强大如我IceGrid-LMLPHP

 打开配置文件 

Ice系列--强大如我IceGrid-LMLPHP

 加载之后生成表单,如下;点击箭头所指按钮,将配置保存到注册中心

Ice系列--强大如我IceGrid-LMLPHP

 可以看到node1下面已经有服务PrinterServer

Ice系列--强大如我IceGrid-LMLPHP

验证服务

执行客户端程序

./client --Ice.Config=config.client

可以看到服务被激活

Ice系列--强大如我IceGrid-LMLPHP

 选择服务右击鼠标,查看标准输出信息:

Ice系列--强大如我IceGrid-LMLPHP

结尾

IceGrid不仅提供了定位服务,负载均衡等功能,同时还简化了服务发布和部署。本文介绍了IceGrid的一个简单应用,后续文章会介绍它的高级部署模式。

01-21 21:15