目录
1、Ice简介
2、Ice下载
3、Slice
3.1、基本数据类型
3.2、常量定义
3.3、复合数据结构定义
3.4、异常定义
3.5、Slice文件复用
3.6、接口和方法定义
4、使用java实现一个实例
4.1、编写脚本
4.2、编写服务端
4.3、编写客户端
Ice是ZeroC的开源通信产品,是一个面向对象的中间件,全程为:The Internet Communications Engine。其作为一款优秀的RPC工具,不仅性能强劲,而且支持多平台多语言开发,即服务端和客户端使用到的开发语言可以是不同的。Ice让我们能够以最小的代价去构建分布式应用,只需要专注业务逻辑即可,底层的通讯逻辑完全由ICE帮我们封装实现。
另一方面,Ice也算是一款老牌RPC框架,其已经有十多年的沉淀,不仅支持服务器端的RPC调用,也支持移动设备,使得开发起来更为方便。其客户端与服务端的架构如下:
该架构图显示应用之间使用Ice作为中间件平台,客户端及服务的的应用都是由应用代码即Ice的库代码混合组成的,且两者使用的开发语言甚至都不同。上述代理是根据Slice定义的ice文件实现,它提供了一个向下调用的接口,提供了数据的序列化与反序列化。Ice的核心部分提供了客户端与服务端的网络连接等核心通信功能,以及其他的网络通信功能的实现及可能问题的处理。
下载Ice的话直接移步官网即可,本文处于mac的环境下,以最新版本3.7为例
https://zeroc.com/downloads/ice/3.7/java
下载完成后使用 slice2java -v 可以查看到当前Ice的版本号。
这是使用Ice所必须编写的一个中间文件,常用来生成代理对象,其全程为 Specification Language for Ice ,作为在服务端与客户端之间调用所使用到的一门中间语言,其有自身的语法,与java其实还挺相似。
类型 |
定义及范围 |
长度 |
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 |
用 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;
类型 |
含义说明 |
enum |
枚举,如: enum Fruit {Apple, Orange} 或: enum Fruit {Apple = 5, Orange = 1} 实际上enum类型等价于int |
struct |
结构体,保护多个属性数据,类似与JavaBean。 struct Student { int id; string name; } |
sequence |
复合类型,支持 基本类型的集合 或者 复合类型的集合,如: sequence sequence 也可以在前面标识出该类型在java中所对应的类型,如: ["java:type:java.util.ArrayList |
dictionary |
Map类型,类似于Java HashMap,如: dictionary |
exception Error {}; // 可定义空异常
exception RangeError {
TimeOfDay errorTime;
TimeOfDay minTime;
TimeOfDay maxTime;
};
使用#include关键字可引用其他slice文件:
#include common.slice
使用interface来申明接口(语法上跟java定义接口语法类似,只是没有public关键字),如:
interface Clock {
TimeOfDay getTime();
void setTime(TimeOfDay time);
}
首先需要编写一个Slice文件,文件必须以.ice结尾,如下定义一个 HelloWorldIDL.ice 文件:
[["java:package:myice.demo"]]
module test {
interface HelloWorldIDL {
string sayHello(string username);
};
};
第一行定义java的父包路径,module定义模块名,因此最后生成的文件的包路径为:myice.demo.test
需要注意的是,在.ice文件 module 部分是不可或缺的,同时任何 } 后都需要跟 ;
然后我们需要使用该文件来生成通用服务类,如下:
slice2java --output-dir ./generated ./slice/HelloWorldIDL.ice
此时在generated目录下即有一大堆java服务类
然后我们新开一个工程,并将上述生成的文件拷入新工程,同时引入开发ice所需要的jar包:
com.zeroc
ice
3.7.2
新建一个服务实现类 HelloWorldHandler
public class HelloWorldHandler implements HelloWorldIDL {
@Override
public String sayHello(String username, Current current) {
// 可以通过Current对象获取到客户端额外透传的参数
Map extraParameters = current.ctx;
String extraStrings = null;
if (!CollectionUtils.isEmpty(extraParameters)) {
StringBuilder builder = new StringBuilder();
extraParameters.forEach((key, value) -> builder.append(key).append("-").append(value).append(";"));
extraStrings = builder.deleteCharAt(builder.length() - 1).toString();
}
return "Hello Zeroc ICE, " + username + ",extra param:" + extraStrings;
}
}
然后再新建一个服务端注册并监听请求的一个类 HelloWorldServer
public class HelloWorldServer {
public static void main(String[] args) {
Communicator communicator = null;
try {
// 初始化ICE Communicator对象,args可以传一些初始化参数,如连接超时、初始化客户端连接池数量等
communicator = Util.initialize(args);
// 使用缺省的通信协议(TCP/IP),用于监听请求
ObjectAdapter adapter = communicator.createObjectAdapterWithEndpoints("helloWorldHandler", "default -p 7890");
// 创建服务端接口处理handler实例(ice里称为servant)
HelloWorldHandler helloWorldHandler = new HelloWorldHandler();
// 将创建的实例添加到adapter中
adapter.add(helloWorldHandler, Util.stringToIdentity("helloWorldHandler"));
// 激活adapter
adapter.activate();
// 在服务退出前,一直监听请求
communicator.waitForShutdown();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭服务
if (null != communicator) {
communicator.destroy();
}
}
}
}
public class HelloWorldClient {
public static void main(String[] args) {
Communicator communicator = null;
try {
// 如服务端一样初始化Communicator对象
communicator = Util.initialize(args);
// 传入远程服务接口的名称、网络协议、IP和端口,创建一个代理对象
ObjectPrx prx = communicator.stringToProxy("helloWorldHandler:default -p 7890");
// 通过slice脚本生成的类(以Prx结尾),向下转型获取到具体的客户端代理类
HelloWorldIDLPrx helloWorldIDLPrx = HelloWorldIDLPrx.checkedCast(prx);
if (null != helloWorldIDLPrx) {
// 在调用方法时此处也可以额外传参到服务端,服务端在Current对象的ctx属性中可以取到
Map params = new HashMap<>();
params.put("key", "value");
String result = helloWorldIDLPrx.sayHello("I'm client", params);
System.out.println(result);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (communicator != null) {
communicator.destroy();
}
}
}
}
然后分别启动服务端与客户端,可以看到控制台打印如下:
Hello Zeroc ICE, I'm client,extra param:key-value
写在末尾,IceBox以及IceGrid是Ice的学习重点,也是Ice精华,有时间且有兴趣需要学习。