Slice介绍
为了开发多语言支持的RPC服务,需要一种中立的新语言来定义这个服务接口,以便各个编程语言能够准确无误地理解和翻译接口,为此Ice设计了Slice语言。Ice开发的第一步就是学习Slice语法,并掌握实际项目开发中所必须的一些技巧。
Slice定义的基本数据类型:
Range of Mapped Type |
Size of Mapped Type |
|
|
|
≥ 1bit |
|
-128-127 or 0-255 a |
≥ 8 bits |
|
-2 15 to 2 15 -1 |
≥ 16 bits |
|
-2 31 to 2 31 -1 |
≥ 32 bits |
|
-2 63 to 2 63 -1 |
≥ 64 bits |
|
IEEE single-precision |
≥ 32 bits |
|
IEEE double-precision |
≥ 64 bits |
|
All Unicode characters, excluding |
Variable-length |
其他类型
Range of Mapped Type |
Size of Mapped Type |
|
|
二进制数组 |
说明:
由于byte在Java里是无符号的,范围是-128~127,因此不建议将byte用作数值参数,而只用作原始字节类型传递二进制数据。
时间类型可以使用long表示自1970年以来的毫秒数。
string长度也是有限的,太长可能导致内存溢出。比如涉及文件传输这样的接口,不宜用字符串一次性传递完文件内容,而应该分批分段传递内容。
除了bool和string其他都和Java一样。
除了基本数据类型,还定义了复合数据类型:
注意:struct不能嵌套struct,但是可以引用struct。struct属性可以设置默认值。strut引用实体的时候变量名和类型不能一样。
例子:
定义常量:
const bool IsSuccess = true;
枚举使用:
enum Fruit { Apple, Pear , Orange}; //enum类型其实等价于int
const Fruit FavoriteFruit = Apple;
定义一个接口:使用关键字interface
struct TimeOfDay{
short hour;
short minute;
short second;
}
interface Clock{
TimeOfDay getTime();
void setTime(TimeOfDay time);
}
支持类似java的Out Parameter 模式,传入一个map或者javabean在方法里面修改,然后方法结束以后可以修改成功。在参数新增out修饰符,即变为出参;(C#也是这样,Java不用修饰符即可)
void changeSleepPeriod(TimeOfDay startTime, out TimeOfDay prevStartTime)
高级技巧:在接口方法上增加Idempotent关键字,表示该方法是幂等的,即调用1次和调用2次其结果是一样的,比如常见的查询操作基本上都是幂等的,而update和create等方法则不是,若一个方法是幂等的,则增加Idempotent修饰后,可以让Ice更好的实现“自动恢复的机制”,即在某个Ice Object调用失败的情况下,无法区分是否已经调用过,但是在因为网络错误导致没有正确返回结果的情况下,Ice会再次调用有Idempotent修饰的方法,透明恢复故障,而在客户端看来则调用正常,没有感觉到Ice做了自动故障恢复的操作。
异常也是可以定义的,语法如下:
exception Error {} ; // 空异常是允许的
exception RangeError{
TimeOfDay errorTime;
TimeOfDay minTime;
TimeOfDay maxTime;
};
多个模块的接口引用共同的数据对象类型的问题,可以将公用的数据对象定义放在一个Slice文件中,如common.sclice,然后其他接口通过关键字#include引用这个文件:
#include common.slice
第一个程序Hello World
1. 新建项目test_ice,右击Ice Builder→Add Ice Builder
2. 定义接口
在slice目录右击New File :myservice.ice,
1
2
3
4
5
6
7
|
[[ "java:package:com.my.demo" ]]
module demo{ //module demo模块名,module和Java的Package是对应的,当前默认package是demo
//如果需要生成和Java对应的package的话,在module上面新增[["java:package:com.my.demo"]]即可,包名为com.my.demo.demo
interface MyService{
string hello();
};
}; |
Ctrl+s 会自己在generated目录下的com.my.demo.demo下生成ice的java源代码
3. 服务端实现
1)在src的service包下新建服务类MyServiceImp继承_MyServiceDisp,并实现Slice定义的接口,实现具体的业务逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package service;
import com.my.demo.demo._MyServiceDisp;
import Ice.Current;
public class MyServiceImpl extends _MyServiceDisp {
private static final long serialVersionUID = 7114601588161119171L;
@Override
public String hello(Current __current) {
return "Hello Jerome" ; //只是简单的返回
}
} |
2)新建启动服务端的代码MyServiceStarter
接下来我们需要新建一个MyServiceImpl实例,这就是之前提到的Servant对象,然后用一个唯一的ID(Ice Object Identity)将其关联到一个Ice Object对象上(ASM过程),最后绑定到一个Object Adapter上,然后我们的客户端(Proxy)就可以通过Object Adapter提供的Endpoint通信端口与这个Servant进行远程通信了。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package service;
public class MyServiceStarter {
public static void main(String[] args) {
int status = 0 ;
Ice.Communicator ic = null ;
try {
// 初始化Communicator对象,args可以传一些初始化参数,如连接超时,初始化客户端连接池的数量等
ic = Ice.Util.initialize(args);
// 创建名为MyServiceAdapter的ObjectAdapter,使用缺省的通信协议(TCP/IP端口为10001的请求)
Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints( "MyServiceAdapter" , "default -p 10001" );
// 实例化一个MyService服务对象(Servant)
MyServiceImpl servant = new MyServiceImpl();
// 将Servant增加到ObjectAdapter中,并将Servant关联到ID为MyService的Ice Object
adapter.add(servant, Ice.Util.stringToIdentity( "MyService" ));
// 激活ObjectAdapter
adapter.activate();
// 让服务在退出之前,一直持续对请求的监听
System.out.println( "server is started ... " );
ic.waitForShutdown();
} catch (Exception e) {
e.printStackTrace();
status = 1 ;
} finally {
if (ic != null ) {
ic.destroy();
}
}
System.exit(status);
}
} |
上述代码的Ice Communicator对象是负责处理网络通信实现RPC调用的,不管在客户端还是服务端都是Communicator这个对象在背后默默地完成无人关注的底层工作。(另外,网络通信的框架代码都是很复杂的,通常内含连接池和线程池,而这两种资源都是有限的资源,并且创建和释放都很耗时,所以要尽量复用这些对象(Communicator)而不是任性的New和Destroy。)
4. 客户端实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package client;
import com.my.demo.demo.MyServicePrx;
import com.my.demo.demo.MyServicePrxHelper;
public class MyClient {
public static void main(String[] args) {
int status = 0 ;
Ice.Communicator ic = null ;
try {
// 初始化通信容器
ic = Ice.Util.initialize(args);
// 传入远程服务单元的名称、网络协议、IP及端口,构造一个Proxy对象
Ice.ObjectPrx base = ic.stringToProxy( "MyService:default -p 10001" );
// 通过checkCast向下转型,获取MyService接口的远程,并同时检测根据传入的名称获取服务单元是否OnlineBook的代理接口
MyServicePrx prxy = MyServicePrxHelper.checkedCast(base);
if (prxy == null ) {
throw new Error( "Invalid proxy" );
}
// 调用服务方法
String rt = prxy.hello();
System.out.println(rt);
} catch (Exception e) {
e.printStackTrace();
status = 1 ;
} finally {
if (ic != null ) {
ic.destroy();
}
}
System.exit(status);
}
} |
MyServicePrx prxy = MyServicePrxHelper.checkedCast(base);//将一个“通用”的Proxy对象转换成一个具体的Proxy对象,原理是“通用”的Proxy里面包括远程Ice Object的Endpoint地址,具体Proxy的Helper类只要用此地址调用一些远程对象,即可判断是否是自己代理的Object了,因此在MyServicePrxHelper.checkedCast的实现逻辑中,包括了一次远程调用的过程,如果确信不会张冠李戴则可以用uncheckedCast(base)方法转换,避免了一次通信过程,有性能上的优势。
运行服务端程序MyServerStarter,然后客户端MyClient 访问即可。
5. Python客户端实现
确保安装了Python环境。然后cmd运行pip install zeroc-ice 安装ice的包,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#!/usr/bin/env python # ********************************************************************** # # Copyright (c) 2003-2013 ZeroC, Inc. All rights reserved. # # This copy of Ice is licensed to you under the terms described in the # ICE_LICENSE file included in this distribution. # # ********************************************************************** import sys, traceback, Ice
Ice.loadSlice( "D:\\Java\\workspace\\workspace-temp\\test_ice\\slice\\myservice.ice" )
import demo
status = 0
ice = None
try :
ic = Ice.initialize(sys.argv)
base = ic.stringToProxy( "MyService:default -p 10001" )
printer = demo.MyServicePrx.checkedCast(base)
if not printer:
raise RuntimeError( "Invalid proxy" )
result = printer.hello()
print result
except :
traceback.print_exc()
status = 1
if ic:
# Clean up
try :
ic.destroy()
except :
traceback.print_exc()
status = 1
sys.exit(status) |
参考
1. 《ZeroC Ice 权威指南》2.4
资料
代码:链接:http://pan.baidu.com/s/1c1bkx8G 密码:j2xw
所有slice数据类型的使用demo代码:链接:http://pan.baidu.com/s/1ntV2sDr 密码:jznd