资源说明:1 系统环境
系统:Win7。
版本:JDK 1.8.0_91,ZeroC ICE 3.6.3。
2 基础
官网:https://zeroc.com
视频教程:https://www.chuanke.com/v6242976-184547-1010808.html
书籍:ZeroC Ice权威指南 高清 带索引书签目录 leader-us(著)
2.1 ICE基础
2.2 Slice
2.2.1 基本数据类型
类型 定义及范围 长度
bool true of false ≥ 1bit
byte [-128, 127] or [0, 255] ≥ 8bit
short [-2^15, 2^15-1] ≥ 16bit
int [-2^31, 2^31-1] ≥ 32bit
long [-2^63, 2^63-1] ≥ 64bit
float ≥ 32bit
double ≥ 64bit
string variable-length
2.2.2 常量定义
用const修饰,如:
const bool trueOrFalse = true;
const byte b = 0x0f;
const string msg = “hello”;
const short s = 56;
const double PI = 3.1416;
enum Fruit {Apple, Orange}; (enum类型其实等价于int)
const Fruit favoriteFruit = Orange;
2.2.3 复合数据结构定义
类型 含义说明
enum 枚举,如:
enum Fruit {Apple, Orange}
或:
enum Fruit {Apple = 5, Orange = 1}
实际上enum类型等价于int
struct 结构体,保护多个属性数据,类似与JavaBean。
struct Student {
int id;
string name;
}
sequence 复合类型,支持 基本类型的集合 或者 复合类型的集合,如:
sequence FruitPlatter;
sequence FruitBanquet; (集合的集合)
dictionary Map类型,类似于Java HashMap,如:
dictionary StudentMap
例:
struct TimeOfDay {
shor hour; // 0 - 23
shor minute; // 0 - 59
shor second; // 0 - 59
};
2.2.4 异常定义
exception Error {}; // 可定义空异常
exception RangeError {
TimeOfDay errorTime;
TimeOfDay minTime;
TimeOfDay maxTime;
};
2.2.5 slice文件复用
使用#include关键字可引用其他slice文件:
#include common.slice
2.2.6 接口和方法定义
使用interface来申明接口(语法上跟java定义接口语法类似,只是没有public关键字),如:
interface Clock {
TimeOfDay getTime();
void setTime(TimeOfDay time);
}
Idempotent关键词:用该关键词修饰方法,指明该方法是幂等的,即调用1次和调用2次其结果是一样的。添加Idempotent修饰的方法,可以让ICE更好地实现“自动恢复错误”机制,即在某个Ice Object调用失败的情况下,ICE会再次调用有Idempotent修饰的方法,透明恢复故障,而在客户端看来则调用正常,没有感觉到ICE做了自动故障恢复操作。
3 实例
参考:http://blog.csdn.net/xuzheng_java/article/details/24459181
所需jar包:
ICE安装目录下lib目录里的ice-3.6.3.jar
3.1 Slice脚本
HelloWorldIDL.ice
[["java:package:myice.demo"]]
module test {
interface HelloWorldIDL {
string sayHello(string username);
};
};
说明:
slice文件必须以ice为后缀。
[["java:package:myice.demo"]]定义java父包路径,module表示模块名,真正生成的包路径为myice.demo.test。
module定义不能缺少。
在定义语句的结尾(如右花括号)需要以分号结尾。
3.2 生成通用服务类
slice2java --output-dir D:\iceoutput D:\slice\HelloWorldIDL.ice
3.3 Server端编写服务实现类
HelloWorldHandler.java
package server;
import Ice.Current;
import myice.demo.test._HelloWorldIDLDisp;
/**
* 接口处理类,继承生成的_HelloWorldIDLDisp类
*/
public class HelloWorldHandler extends _HelloWorldIDLDisp {
private static final long serialVersionUID = 1L;
/*
* 在__current.ctx里可获取到客户端额外上送的参数
*/
@Override
public String sayHello(String username, Current __current) {
return "Hello ZeroC ICE, [" + username + "]";
}
}
HelloIceHandler.java
package server;
import Ice.Current;
import myice.demo.test._HelloWorldIDLDisp;
/**
* 接口处理类,继承生成的_HelloWorldIDLDisp类
*/
public class HelloIceHandler extends _HelloWorldIDLDisp {
private static final long serialVersionUID = 1L;
/*
* 在__current.ctx里可获取到客户端额外上送的参数
*/
@Override
public String sayHello(String username, Current __current) {
return "Hello ICE ASDFSDF, [" + username + "]";
}
}
3.4 Server端注册服务并监听请求
package server;
public class HelloWorldServer {
public static void main(String[] args) {
Ice.Communicator communicator = null;
try {
// 初始化ICE Communicator对象,args可以传一些初始化参数,如连接超时、初始化客户端连接池数量等
communicator = Ice.Util.initialize(args);
// 创建ObjectAdapter(名称为helloWorldAdapter),使用缺省的通信协议(TCP/IP),端口为7890,用于监听请求
Ice.ObjectAdapter adapter = communicator.createObjectAdapterWithEndpoints("helloWorldAdapter", "default -p 7890");
// 创建服务端接口处理handler实例(ice里称为servant)
HelloWorldHandler helloWorldHandler = new HelloWorldHandler();
/*
* 将helloWorldHandler添加到ObjectAdapter中,并将helloWorldHandler关联到ID为"helloWorldHandler"的Ice Object
* (此处ID相当于接口名称,全局唯一,client端通过该名称连接上来)
*/
adapter.add(helloWorldHandler, Ice.Util.stringToIdentity("helloWorldHandler"));
adapter.activate(); // 激活ObjectAdapter
// 在服务退出前,一直监听请求
communicator.waitForShutdown();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (communicator != null) {
communicator.destroy();
}
}
}
}
3.5 Client发送请求并得到响应
package client;
import myice.demo.test.HelloWorldIDLPrx;
import myice.demo.test.HelloWorldIDLPrxHelper;
public class HelloWorldClient {
public static void main(String[] args) {
Ice.Communicator communicator = null;
try {
// 初始化ICE Communicator对象,args可以传一些初始化参数,如连接超时、初始化客户端连接池数量等
communicator = Ice.Util.initialize(args);
// 传入远程服务接口的名称、网络协议、IP和端口,创建一个Proxy对象
Ice.ObjectPrx base = communicator.stringToProxy("helloWorldHandler:default -p 7890");
/*
* 通过checkedCast向下转型,获取HelloWorld接口的代理类(客户端)
* 其中:HelloWorldIDLPrx和HelloWorldIDLPrxHelper为生成的类
*/
HelloWorldIDLPrx helloWorldClient = HelloWorldIDLPrxHelper.checkedCast(base);
if (helloWorldClient != null) {
String result = helloWorldClient.sayHello("ZEROC ICE");
// 可额外传参到服务端,服务端在Current的ctx属性里可获取到传参值。如:
// Map params = new HashMap();
// params.put("param1", "aaaaaaa");
// params.put("param2", "bbbbbbb");
// String result = helloWorldClient.sayHello("ZEROC ICE", params);
System.out.println(result);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (communicator != null) {
communicator.destroy();
}
}
}
}
3.6 运行
运行HelloWorldServer,启动服务。
运行HelloWorldClient,调用远程服务接口,控制台打印如下:
Hello ZeroC ICE, [ZEROC ICE]
成功调用远程服务。
4 IceBox
IceBox类似于Tomcat,里面可以加载多个服务器实例(应用)。本章节示范一个IceBox容器的开发过程。
所需jar包:icebox-3.6.3.jar
4.1 服务器实例编写
一个IceBox服务器实例继承自IceBox.Service接口:
package icebox;
import Ice.Communicator;
import IceBox.Service;
import server.HelloIceHandler;
import server.HelloWorldHandler;
/**
* IceBox服务器实例,实现IceBox.Service接口
*/
public class HelloWorldServer2 implements Service {
private Ice.ObjectAdapter adapter = null;
@Override
public void start(String name, Communicator communicator, String[] arg2) {
System.out.println("ice box helloworld starting...");
adapter = communicator.createObjectAdapter(name);
// 创建服务端接口处理handler实例(ice里称为servant)
HelloWorldHandler helloWorldHandler = new HelloWorldHandler();
HelloIceHandler helloIceHandler = new HelloIceHandler();
// 将Handler添加到ObjectAdapter中
adapter.add(helloWorldHandler, Ice.Util.stringToIdentity("helloWorldHandler2"));
adapter.add(helloIceHandler, Ice.Util.stringToIdentity("helloIceHandler2"));
adapter.activate(); // 激活ObjectAdapter
System.out.println("ice box helloworld started.");
}
@Override
public void stop() {
adapter.destroy();
System.out.println("ice box helloworld stoped.");
}
}
4.2 配置文件编写
编写icebox.properties配置文件,建议放在classpath里,示例配置如下:
#icebox instance properties begin
IceBox.InstanceName=MyIceBox 1
IceBox.InheritProperties=1
IceBox.PrintServicesReady=MyAppIceBox 1
#icebox instance properties end
#log define begin
Ice.Trace.Network=1
Ice.Trace.ThreadPool=1
Ice.Trace.Locator=1
#log define end
#server define begin
IceBox.Service.HelloWorldServer2=icebox.HelloWorldServer2 prop1=1 prop2=2 prop3=3
IceBox.UseSharedCommunicator.HelloWorldServer2=1
HelloWorldServer2.Endpoints=tcp -h localhost -p 7890
#server define end
4.3 运行
4.3.1 启动IceBox容器
在eclipse里调试
运行IceBox容器,可通过java命令运行,也可在eclipse里调试:
在项目上右击->Debug As->Debug configurations,新建一个debug configuration,配置信息如下:
Main->Name:根据需要填写,如IceBoxTest
Main->MainClass:IceBox.Server
Arguments->program arguments:--Ice.Config=icebox.properties
点击debug按钮,即可启动MainClass进行调试。
通过代码启动
package icebox;
public class StartIceBox {
public static void main(String[] args) {
IceBox.Server icebox = new IceBox.Server();
icebox.main(new String[] {"--Ice.Config=icebox.properties"});
}
}
4.3.2 运行客户端
运行章节3.5即可。
5 IceGrid
IceGrid为ICE提供的分布式系统架构,由一个Ice Registry + N个Ice Grid Node组成。
5.1 Ice Registry
Ice Registry(注册表)组件用于存储服务的Endpoint信息,支持主从同步,从节点可以分担查询请求,类似MySQL的读写分离,并防止单点故障。Registry以二进制文件形式存储运行期Ice服务注册信息。
IceBox可以注册到Registry上,客户端通过向Registry发送请求,得到具体的服务的Endpoint信息,然后建立起服务之间的直连TCP通道,此后的数据交互就在这个直连通道上完成。
本节示例配置一个Registry组件,并注册一个IceBox服务到Registry上,编写客户端通过该Registry来访问服务,此处Registry仅提供查询服务的功能。
5.1.1 启动Registry
配置文件
编写配置文件:icegridregistry.cfg
IceGrid.InstanceName=MyIceGrid #默认名称是IceGrid
IceGrid.Registry.Client.Endpoints=tcp -h 192.168.1.7 -p 4061
IceGrid.Registry.Server.Endpoints=tcp -h 192.168.1.7
IceGrid.Registry.Internal.Endpoints=tcp -h 192.168.1.7
IceGrid.Registry.Data=D:\Program Files (x86)\ZeroC\data\registry #路径需手动创建好
IceGrid.Registry.PermissionsVerifier=MyIceGrid/NullPermissionsVerifier
IceGrid.Registry.AdminPermissionsVerifier=MyIceGrid/NullPermissionsVerifier
IceGrid.Registry.DynamicRegistration=1
启动
进入ice安装目录下的bin子目录,启动registry,如:
D:\Program Files (x86)\ZeroC\Ice-3.6.3\bin>
icegridregistry --Ice.Config=D:\ZerocICE\publish\rsrc\icegridregistry.cfg
启动后,可在IceGrid.Registry.Data目录下看到生成的一堆二进制文件。
5.1.2 注册IceBox到Registry
注册IceBox到Registry,只需在原有的IceBox配置文件(4.1)上添加如下配置,启动IceBox即可:
#iceboxregistry define begin
Ice.Default.Locator=MyIceGrid/Locator:tcp -h 192.168.1.7 -p 4061
HelloWorldServer2.AdapterId=HelloWorldServer2
#iceboxregistry define end
其中:
MyIceGrid为registry里定义的IceGrid.InstanceName属性值,其后的ip port为 registry的ip port。
HelloWorldServer2.AdapterId=HelloWorldServer2格式为:{serviceName}.AdapterId={adapterName},本例serviceName和adapterName均为IceBox配置里IceBox.Service.{serviceName}上的serviceName值。
5.1.3 Client发送请求并获得响应
修改3.5里的获取代理类的代码即可。完整代码如下:
package client;
import myice.demo.test.HelloWorldIDLPrx;
import myice.demo.test.HelloWorldIDLPrxHelper;
public class HelloWorldClient4IceGrid {
public static void main(String[] args) {
Ice.Communicator communicator = null;
try {
communicator = Ice.Util.initialize(new String[] {"--Ice.Default.Locator=MyIceGrid/Locator:tcp -h 192.168.1.7 -p 4061"});
Ice.ObjectPrx base = communicator.stringToProxy("helloWorldHandler2@HelloWorldServer2");
HelloWorldIDLPrx helloWorldClient = HelloWorldIDLPrxHelper.checkedCast(base);
if (helloWorldClient != null) {
String result = helloWorldClient.sayHello("fffkkk");
System.out.println(result);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (communicator != null) {
communicator.destroy();
}
}
}
}
5.1.4 运行
启动IceBox服务,注册HelloWorldServer2到Registry。
运行HelloWorldClient4IceGrid,调用远程服务接口,控制台打印如下:
Hello ZeroC ICE, [fffkkk]
成功调用远程服务。
5.2 IceGrid Node
IceGrid Node为IceGrid集群里的节点,负责IceBox的装载和启停、采集主机的负载信息和执行控制器IceGrid Admin的指令。
IceGrid Node是IceBox的容器,IceBox是服务(如HelloWorldServer2)的容器。一个IceGrid Node可以部署多个IceBox实例,这些实例可以是同一个IceBox的多个实例也可以是多个不同IceBox的实例组合。IceGrid Node连同其上运行的IceBox实例的信息同步到了Registry中。
每个IceGrid Node都有唯一的名字,用来在IceGrid中标识其身份,可以在用一个服务器上部署多个IceGrid Node,只要确保其都有唯一的名字即可。每个IceGrid Node都有一个配置文件,定义了其启动参数。
5.2.1 启动IceGrid Node
配置文件
编写配置文件:icegridnode.cfg
Ice.Default.Locator=MyIceGrid/Locator:tcp -h 192.168.1.7 -p 4061 # 指定Ice Registry信息
IceGrid.Node.Name=node1
IceGrid.Node.Endpoints=tcp -h 192.168.1.7
IceGrid.Node.Data=D:\Program Files (x86)\ZeroC\data\node1 # 所有目录需预先创建好
IceGrid.Node.Output=D:\Program Files (x86)\ZeroC\data\node1
Ice.StdErr=D:\Program Files (x86)\ZeroC\data\node1\stderr.log
Ice.StdOut=D:\Program Files (x86)\ZeroC\data\node1\stdout.log
启动
进入ice安装目录下的bin子目录,启动registry,如:
D:\Program Files (x86)\ZeroC\Ice-3.6.3\bin>
icegridnode --Ice.Config=D:\ZerocICE\publish\rsrc\icegridnode.cfg
启动后,可在IceGrid.Node.Data目录下看到生成的一堆文件。
5.3 IceGrid集群
系统环境
CentOS 7,jdk1.8.0_91,zeroc ice 3.6.3
系统拓扑
服务器 节点类型 服务
192.168.1.210 Ice Registry 1个Ice Registry、1个IceBox
192.168.1.212 IceGrid Node 1个IceBox
192.168.1.213 IceGrid Node 1个IceBox
服务器安装ice
CentOS下可安装Red Hat版本ICE:
# cd /etc/yum.repos.d
# wget https://zeroc.com/download/rpm/zeroc-ice-el7.repo
# yum install ice-all-runtime ice-all-devel
5.3.1 icegridadmin使用
icegridadmin为IceGrid集群管理工具,用于部署或升级IceGrid、查看IceGrid节点状态、启停Node中的Server实例等。运行icegridadmin,需要先启动Registry和Node。
启动icegridadmin
进入ice安装目录下的bin子目录,运行如下命令:
D:\Program Files (x86)\ZeroC\Ice-3.6.3\bin>
icegridadmin -u test -p test --Ice.Default.Locator="MyIceGrid/Locator:tcp -h 192.168.1.7 -p 4061"
即可进入icegridadmin操作符模式。由于Registry在本地且没开启权限验证,所以可用test/test用户名及密码来访问 Registry注册表。
5.3.2 集群搭建(暂单服务器)
首先将程序包分发到各个服务器上,根据配置文件建立必要的目录。
IceBox代码
参照第3章和第4.1章节,完成IceBox服务器代码的编写。
此时不需要IceBox的配置文件,将由IceGrid自动生成。
启动Ice Registry
参考5.1.1章节,在192.168.2.10上启动icegridregistry服务:
# icegridregistry --daemon --Ice.Config=/root/iceserver/rsrc/icegridregistry.cfg
启动后可使用一下命令查看启动后运行在后台的进程:
# ps aux | grep ice
配置文件示例:
[root@nn rsrc]# more icegridregistry.cfg
IceGrid.InstanceName=MyIceGrid
IceGrid.Registry.Client.Endpoints=tcp -h 192.168.1.210 -p 4061
IceGrid.Registry.Server.Endpoints=tcp -h 192.168.1.210
IceGrid.Registry.Internal.Endpoints=tcp -h 192.168.1.210
IceGrid.Registry.Data=/root/iceserver/data/registry
IceGrid.Registry.PermissionsVerifier=MyIceGrid/NullPermissionsVerifier
IceGrid.Registry.AdminPermissionsVerifier=MyIceGrid/NullPermissionsVerifier
IceGrid.Registry.DynamicRegistration=1
启动IceGrid Node
参考5.2.1章节,启动icegridnode服务:
# icegridnode --daemon --Ice.Config=/root/iceserver/rsrc/icegridnode.cfg
各个服务的配置可根据需要做微调,如节点目录。
配置文件示例:
[root@nn rsrc]# more icegridnode.cfg
# 指定Ice Registry信息
Ice.Default.Locator=MyIceGrid/Locator:tcp -h 192.168.1.210 -p 4061
IceGrid.Node.Name=node1
IceGrid.Node.Endpoints=tcp -h 192.168.1.210
IceGrid.Node.Data=/root/iceserver/data/node1
IceGrid.Node.Output=/root/iceserver/data/node1
Ice.StdErr=/root/iceserver/data/node1/stderr.log
Ice.StdOut=/root/iceserver/data/node1/stdout.log
启动IceGrid集群
编写集群配置文件:
myicegrid.xml
CLASSPATH=/usr/java/jdk1.8.0_91/lib/tools.jar;/root/iceserver/lib/*
进入icegridadmin命令行:
# icegridadmin -u test -p test --Ice.Default.Locator="MyIceGrid/Locator:tcp -h 192.168.1.210 -p 4061"
载入应用:
>>> application add /root/iceserver/rsrc/myicegrid.xml
更新/重新载入:
>>> application update /root/iceserver/rsrc/myicegrid.xml
启动服务(启动服务出错,待解决):
>>> server start MyServer-1
常用命令:
server list 查看启动的服务器
service list MyServer-1 查看MyServer-1上的服务
service describe MyServer-1 HelloWorldServer2 查看MyServer-1下HelloWorldServer2服务的描述
adapter list 查看所有Adapter
node list 查看所有node
6 常见问题
本源码包内暂不包含可直接显示的源代码文件,请下载源码包。