一、 简介
Ice版本:3.1.1
使用iceGrid服务实现一个简单的打印系统,主要说明iceGrid的使用方法,系统部署在4台主机上,注册器和打印服务器安装在同一台主机上,如下图所示:
下图显示了一个客户端通过间接代理的初始调用,来激活目标服务器。
在这个架构当中不需要花时间手动启动服务。客户端的寻址请求提示注册器去查询节点的服务器状态并且根据需要启动它。一旦这个服务启动成功,这个寻址请求完成,并且随后客户端直接和服务端通信。
注册器需要一个子目录,在这个目录中创建它的数据库,由IceGrid.Registry.Data指定,此目录必须在启动注册器前已经存在 。
# Registry properties IceGrid.Registry.Client.Endpoints=tcp -p 4061 IceGrid.Registry.Server.Endpoints=tcp IceGrid.Registry.Internal.Endpoints=tcp IceGrid.Registry.AdminPermissionsVerifier=IceGrid/NullPermissionsVerifier IceGrid.Registry.PermissionsVerifier=IceGrid/NullPermissionsVerifier IceGrid.Registry.Data=F:\lhy_workspace\ICETest\src\iceGrid\registry
IceGrid.Registry.Admin.Endpoints=default
# Node properties IceGrid.Node.Endpoints=tcp IceGrid.Node.Name=Node1 IceGrid.Node.Data=F:\lhy_workspace\ICETest\src\iceGrid\note IceGrid.Node.CollocateRegistry=1 Ice.Default.Locator=IceGrid/Locator:tcp -p 4061 |
1、注册器和节点1的配置信息在同一个配置文件中,在节点1的服务器上存储该配置文件,如存放在c盘根目录下c:\config,文件名为config。
2、将服务器应用程序以及其依赖的jar包拷贝至节点1相应的目录下,如下文中部署文件配置的C:\PrinterApp.jar。
有几个属性定义了Endpoints,但只有 icegrid.registry.client.endpoints的值需要一个固定的端口。此属性指定IceGrid定位服务的端口; IceGrid的客户端必须在他们的Ice.Default.Locator中包括这个端口。
其他的一些属性:
属性 |
说明 |
IceGrid.Registry.AdminPermissionsVerifier |
这两个属性控制访问Registry的管理权限 |
IceGrid.Registry.PermissionsVerifier |
|
IceGrid.Registry.Data |
这个属性指定Registry的数据库目录,用于存放注册器的一些状态信息,可以任意指定。 |
IceGrid.Registry.DynamicRegistration |
通过将此属性设置为一个非零值,我们允许服务器注册对象适配器 |
IceGrid.Registry.Admin.Endpoints |
指定IceGrid的Admin代理端点, |
IceGrid.Node.CollocateRegistry |
如果值被设置为大于零的值,说明该节点和注册表运行在同一个进程中,该节点和注册器使用相同的配置文件。 |
在使用icegridadmin命令管理icegrid时,缺少这项配置,系统会抛出异常:
no suitable endpoint available for proxy `IceGrid/Admin -t' |
IceGrid.Node.Endpoints=tcp IceGrid.Node.Name=Node2 IceGrid.Node.Data=F:\lhy_workspace\ICETest\src\iceGrid\note2 Ice.Default.Locator=IceGrid/Locator:tcp -h 192.168.1.193 -p 4061 |
1、在节点2的服务器上存储该配置文件,如存放在c盘根目录下c:\config,文件名为config。
2、将服务器应用程序以及其依赖的jar包拷贝至节点2相应的目录下,如下文中部署文件配置的C:\PrinterApp.jar。
属性 |
说明 |
IceGrid.Node.Endpoints |
配置节点的端点,不需要指定固定的端口 |
IceGrid.Node.Name |
为节点定义一个不同的名字 |
IceGrid.Node.CollocateRegistry |
不能有两个节点都配置这个属性只能有一个主Registry,所以此属性在这个节点中不需要再配置 |
IceGrid.Node.Data |
指定该节点的数据目录,用于存储该节点的状态信息,可以任意指定 |
打印服务器的Slice脚本如下
module Demo {
interface Printer {
string printString(string s);
};
};
打印服务器实现简单的打印功能,服务端接收客户端的打印信息,并负责将信息打印到终端,打印完成之后,返回给客户端一个打印完成的提示信息。
package iceGrid.sampleAdapterLocator.servant;
public class PrinterI extends Demo._PrinterDisp {
public String printString(String s, Ice.Current current) {
System.out.println(s);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "打印完成";
}
}
package iceGrid.sampleAdapterLocator.server;
import iceGrid.sampleAdapterLocator.servant.PrinterI;
public class Server extends Ice.Application {
public int run(String[] args) {
//创建名为SimplePrinterAdapter的适配器,
Ice.ObjectAdapter adapter = communicator().createObjectAdapter("PrinterAdapter");
//实例化一个PrinterI对象,为Printer接口创建一个服务对象
Ice.Object object = new PrinterI();
//将服务单元增加到适配器中,并给服务对象指定名称为SimplePrinter,该名称用于唯一确定一个服务单元
adapter.add(object, Ice.Util.stringToIdentity("SimplePrinter"));
//激活适配器,这样做的好处是可以等到所有资源就位后再触发
adapter.activate();
//让服务在退出之前,一直持续对请求的监听
communicator().waitForShutdown();
return 0;
}
public static void main(String[] args) {
Server app = new Server();
System.out.println("服务器已经启动!");
System.exit(app.main("Server", args));
}
}
package iceGrid.sampleAdapterLocator.client;
public class Client extends Ice.Application {
public int run(String[] args) {
//获取Printer的远程代理,这里使用的stringToProxy方式
Ice.ObjectPrx base = communicator().stringToProxy("SimplePrinter");
//通过checkedCast向下转换,获取Printer接口的远程,并同时检测根据传入的名称获取的服务单元是否Printer的代理接口,如果不是则返回null对象
Demo.PrinterPrx printer = Demo.PrinterPrxHelper.checkedCast(base);
if (printer == null) throw new Error("Invalid proxy");
//把Hello World传给服务端,让服务端打印出来,因为这个方法最终会在服务端上执行
for(int i = 0;i<10;i++){
String ret = printer.printString("Hello World!哈哈");
System.out.println(ret);
}
return 0;
}
public static void main(String[] args) {
Client app = new Client();
System.exit(app.main("Server", args,"C:\\client.conf"));
}
}
客户端使用stringToProxy("SimplePrinter")方式,获取服务单元在本地的代理对象,仅仅使用服务单元的对象标识SimplePrinter,客户端不需要指定请求的服务器以及对象适配器的端点信息,客户端使用标识第一次发送请求到注册器,由注册器根据这个唯一的标识,给客户端分配一个对象适配器端点,然后客户端在与端点建立连接。
客户端调用 checkedCast 是在这个printer对象上的第一个远程调用,因此,这个定位请求在这个调用过程中被执行。随后调用 printString是直接发送给服务器而没有IceGrid的进一步参与。
Ice.Default.Locator=IceGrid/Locator:tcp -h 192.168.1.193 -p 4061 |
客户端要与注册器建立连接,首先要在客户端配置注册器的位置信息。客户端配置文件存放在C:\client.conf,
属性 |
说明 |
Ice.Default.Locator |
用于客户端Ice run time定位服务代理。在IceGrid中 ,定位服务是由注册器承担,定位对象位于注册器的client endpoionts。此属性的端口值参考注册器配置文件中的 IceGrid.Registry.Client.Endpoints, -h指定注册器所在的主机IP地址,定位对象默认为 IceGrid/Locator |
replica-group:定义一个适配器复制组,每一个应用中的适配器通过使用adapter标签中的replica-group属性,将适配器加入到这个复制组中,如:
replica-group="PrinterAdapters" endpoints="tcp" />
name= PrinterAdapter表明该适配器是复制组的成员。当环境中同时部署了几个节点时,客户端向服务器发出定位请求,客户端不再需要使用IceGrid.Query接口来查询出服务器中有哪些端点,使用replica-group标签在集群环境中的注册器会返回所有对象适配器的端点给客户端,客户端的Ice run time随机选择一个端点与服务器进行通信。
load-balancing:配置集群环境中的负载均衡。type属性的取值有Random: 注册器随机选择一个适配器处理客户端的请求,注册器不会为复制组(replica group)考虑系统的负载情况;Adaptive:注册器根据系统的负载信息来选择一个负载最低的对象适配器,使用这种类型,还需要load-sample属性配合,此属性规定每一个节点,在规定的取样间隔时间(一、五、十五分钟),向注册器上报系统的负载信息;round-robin:注册器选择最近最少使用的适配器;Ordered:注册器通过适配器的优先级来选择,可以设置一个优先级为复制组中的每个对象适配器成员。
Object:向注册器中注册可以被客户端请求的对象。identity 属性指定该对象的唯一标示;Type属性指定该对象在应用程序中的类型,在上面的例子中,该类型在C:\PrinterApp.jar包中。
server-template:服务器的模板标签,可以在每个节点中部署服务,使用如下方式:
节点Node1和节点Node2中分布部署了相同的服务,使用了两次server标签,为了简化部署过程,可以使用server-template标签,类似这样的结构:
……
Parameter:定义一个参数。
Server:定义一个服务,属性id中的${index}被server-instance标签中给出的index="1"值代替;exe属性指定启动应用的执行命令,如exe="java"通过该命令执行option标签中的jar包,相当于命令java –jar C:\PrinterApp.jar;activation属性指定服务是否按需启动,在应用程序部署完成之后,服务可以不需要预先启动,当客户端有请求到达时,由节点负责激活该服务,activation属性是ondemand ,表明是根据需要来激活这个服务。
Option:配置启动服务需要的参数,以及应用程序的完成路径。
Adapter:注册适配器。replica-group属性指定该适配器属于复制组的成员,复制组在前面已经被定义;endpoints属性指定端点使用的协议,此处不需要指定具体的端口,端口由注册器负责分配,这个值不包括任何端口信息,意味着这个适配器使用系统分配的端口.客户可以使用不包含端点的间接代理。它的name和id属性都指定为PrinterAdapter 。name的值反映的是这个适配器在服务进程中的名字 (即createObjectAdapter所传递的参数),这就是他被用作配置的用途,而在注册器中,id的值唯一的标识了这个适配器,并且被用在间接代理中. 这两个名字并不要求一定一样,如果我们忽略这个id属性,IceGrid将会结合server的名字和adapter的名字组成一个唯一值,产生如下的标识:
PrinterServer.PrinterAdapter
Properties:设置属性,可以使用此标签为每个服务实例的开启指定参数,如:使用参数Ice.Trace.Network设置网络跟踪级别
Node:配置节点,一个节点下,可以配置多个服务实例。
server-instance:配置服务实例。template属性指定前面已经定义的服务模板,index指定该服务实例的唯一标识,将替换server标签中的${index}。
现在一个配置文件已经写好,并且相关的的目录结构已经被预先准备,开始启动Registry和Node。使用一个Registry和Node在一起的情况,我们只需要使用一个命令启动:icegridnode --Ice.Config=config
1、 启动注册器并注册节点1
在192.168.1.193主机上的CMD命令行中,进入到注册器配置文件所在
目录,启动注册器并注册节点1,使用命令:icegridnode --Ice.Config=config
2、注册节点2
在192.168.1.11主机上,按照上面的操作执行,注册节点2到注册表中,使用命令:icegridnode --Ice.Config=config
在Registry启动并运行的情况下,使用icegridadmin命令行工具部署应用。
在192.168.1.193主机上,在打开一个CMD命令行,启动管理工具,使用命令:icegridadmin --Ice.Config=config,和客户端一样,icegridadmin工具需要Ice.Default.Locator属性,所以需要指定该属性所在的配置文件,
确认可以连上Registry后,icegridadmin提供了一个命令行提示符,在命令行提示符中可以部署我们的应用。假如部署文件在c盘根目录下C:\app.xml,启动icegridadmin工具之后,使用如下命令部署:
>>>application add "C:\app.xml"
或者直接在命令行使用:icegridadmin --Ice.Config=config -e "application update C:\app.xml"。
如果部署文件被修改过,需要重新部署,可以使用命令application update "C:\app.xml"。
确认应用已经被成功部署:
>>>application list
PrinterApplication
查看对象适配器的当前端点信息:
>>>adapter endpoints PrinterServer1.PrinterAdapter
dummy -t:tcp -h 192.168.1.193 -p 1933
如果节点中的服务还没有被激活,则显示
系统已经部署完成。运行在199和144主机上的客户端,可以看到服务器上的cmd窗口中打印出信息。如下图:
193主机上打印信息。
11主机上打印信息。
参考《Distributed Programming with Ice》,IceGrid学习笔记(待续),无处不在 第39章IceGrid 第3节IceGrid构架(翻译)