转自:http://blog.csdn.net/zhu2695/article/details/51494664java
一、ICE是什么?
ICE是ZEROC的开源通讯协议产品,它的全称是:The Internet Communications Engine,翻译为中文是互联网通讯引擎,是一个面向对象的中间件,使咱们可以以最小的代价构建分布式应用程序。ICE使咱们专一于应用逻辑的开发,它来处理全部底层的网络接口编程,这样咱们就不用去考虑这样的细节:打开网络链接、网络数据传输的序列化与反序列化、链接失败的尝试次数等。
二、为何会有ICE?
ICE是分布式应用的一种比较好的解决方案,虽然如今也有一些比较流行的分布式应用解决方案,如微软的.NET(以及原来的DCOM)、CORBA及WEB SERVICE等,可是这些面向对象的中间件都存在一些不足:
.NET是微软产品,只面向WINDOWS系统,而实际的状况是在当前的网络环境下,不一样的计算机会运行不一样的系统,如LINUX上面就不可能使用.NET;
CORBA虽然在统一标准方面作了不少的工做,可是不一样的供应商实现之间仍是缺少互操做性,而且目前尚未一家供应商能够针对全部的异种环境提供全部的实现支持,且CORBA的实现比较复杂,学习及实施的成本都会比较高;
WEB SERVICE最要命的缺点就是他的性能问题,对于要求比较高的行业是不多会考虑WEB SERVICE的。
ICE的产生就是源于.NET、CORBA及WEB SERVICE这些中间件的不足,它能够支持不一样的系统,如WINDOWS、LINUX等,也能够支持在多种开发语言上使用,如C++、C、JAVA、RUBY、PYTHON、VB等,服务端能够是上面提到的任何一种语言实现的,客户端也能够根据本身的实际状况选择不一样的语言实现,如服务端采用C语言实现,而客户端采用JAVA语言实现,底层的通信逻辑经过ICE的封装实现,咱们只须要关注业务逻辑。
三、ICE是如何工做的?
Ice 是一种面向对象的中间件平台,这意味着 Ice为构建面向对象的客户-服务器应用提供了工具、API 和库支持。要与Ice持有的对象进行通讯,客户端必须持有这个对象的代理(与CORBA的引用是相同的意思),这里的代理指的是这个对象的实例,ICE在运行时会定位到这个对象,而后寻找或激活它,再把In参数传给远程对象,再经过Out参数获取返回结果。
这里提到的代理又分为直接代理和间接代理,直接代理其内部保存有某个对象的标识,以及它的服务器的运行地址;间接代理指的是其内部保存有某个对象的标识,以及对象适配器名(object adapter name),间接代理没有包含寻址信息,为了正确地定位服务器,客户端在运行时会使用代理内部的对象适配器名,将其传给某个定位器服务,好比IcePack服务,而后,定位器会把适配器名看成关键字,在含有服务器地址的表中进行查找,把当前的服务器地址返回给客户,客户端 run time如今知道了怎样联系服务器,就会像日常同样分派 (dispatch)客户请求。
ICE能够保证在任何的网络环境或者操做系统下,成功的调用只有一次,它在运行时会尽力的定位到远程服务器,在链接失败的状况下会作尝试性重复性链接,确实连不上的状况会给用户以提示。
客户端在调用服务端的方法时,能够采起同步或异步的方式实现,同步调用就至关于调用本身本地的方法同样,其它行为会被阻塞;异步调用是很是有用的调用方式,如服务端须要准备的数据来自于其它异步接口,这个时候客户端就不须要等待,待服务端数据准备充份后,以消息的方式通知客户端,服务端就能够去干其它的事情了,而客户端也能够到服务端获取数据了。
四、ICE调用模式
ICE采用的网络协议有TCP、UDP以及SSL三 种,不一样于WebService,ICE在调用模式上有好几种选择方案,而且每种方案正对不一样的网络协议的特性作了相应的选择。
Oneway(单向调用):客户端只需将调用注册到本地传输缓冲区(Local Transport Buffers)后就当即返回,不会等待调用结果的返回,不对调用结果负责。
Twoway(双向调用):最通用的模式,同步方法调用模式,只能用TCP或SSL协议。
Datagram(数据报):相似于Oneway调用,不一样的是 Datagram调用只能采用UDP协议并且只能调用无返回值和无输出参数的方法。
BatchOneway(批量单向调用):先将调用存 在调用缓冲区里面,到达必定限额后自动批量发送全部请求(也可手动刷除缓冲区)。
BatchDatagram(批量数据报):与上相似。
不一样的调用模式其实对应着不动的业务,对于大部分的有返回值的或须要实时响应的方法,咱们可能都采用Twoway方式调用,对于一些无需返回值或 者不依赖返回值的业务,咱们能够用Oneway或者BatchOneway方式,例如消息通知;剩下的Datagram和BatchDatagram方式 通常用在无返回值且不作可靠性检查的业务上,例如日志。
五、客户端与服务端的结构
这个图示显示了使用ICE作为中间件平台,客户端及服务端的应用都是由应用代码及ICE的库代码混合组成的。
客户应用及服务器应用分别对应用的是客户端与服务端。
代理是根据SLICE定义的ice文件实现,它提供了一个向下调用的接口,提供了数据的序列化与反序化。
ICE的核心部份,提供了客户端与服务端的网络链接等核心通讯功能,以及其它的网络通讯功能的实现及可能的问题的处理,让咱们在编写应用代码的时候没必要要去关注这一块,而专一于应用功能的实现。
6. 要使用ICE,必须先安装ICE,安装及配置参考以下:
WINDOWS:http://blog.csdn.net/fenglibing/archive/2011/04/28/6368665.aspx
LINUX(BDB的安装还有问题,没法使用SLICE2JAVA):http://blog.csdn.net/fenglibing/archive/2011/04/27/6367559.aspx
这个示例是JAVA示例,是从ICE的帮助文档中摘出来的,是一个输出Hello World的测试程序,采用的ICE版本是3.1.1。1)、准备一个ice文件并命名为:Printer.ice,其内容为:
module Demo {
interface Printer {
void printString(string s);
};
};
2)、转到命令行,在Printer.ice文件保存目录执行命令:
slice2java Printer.ice
会在目录下面生成一个Demo 文件夹,里面会生成一些JAVA文件,以下图示:
3)、这些文件的类图结构以下:
这里对生成的一些文件作些解释,分两两部份,服务端类文件及客户端类文件:
• <interface-name>.java
这个源文件声明在ICE文件中定的接口名称的Java接口,如这里是Printer。
• _<interface-name>Operations.java
_<interface-name>OperationsNC.java
这是两个定义操做的接口文件,每一个接口文件中定义了一个操做实现,定义的操做与Slice接口中定义的操做相一致,只是在_<interface-name>Operations.java中定义的方法多了一个参数“Ice.Current __current”(注:Current对象的定义,请参见3.1.1版本文档中的31.6 The Ice::Current Object说明),这个参数的做用是能够容许咱们访问 “正在执行的请求”和 “服务器中的操做的实现”等信息,也就是咱们的请求需求须要其它请求的支持时或者要获取其它请求的执行结果时,咱们能够调用这个方法,这两个接口文件都会被接口文件_<interface-name>.java继承。
• _<interface-name>Disp.java 这个文件包含的是服务器端骨架类的定义,所用接口定义都要继承这个东西,这里的接口指供客户端调用的接口。
• <interface-name>PrxHolder.java 代理定义holder 类,是对应Out参数使用的。通常参数都是值传递,这个类的做用是使参数经过引用传递。ICE框架应用了不少反射机制,这个类是改变远程参数的一个映射。
• _<interface-name>Del.java
• _<interface-name>DelD.java
• _<interface-name>DelM.java
不用关心上面的这些文件,这些文件包含的是供Java 映射内部使用的代码;它们包含的功能与应用程序无关。
• <interface-name>Prx.java 这个是代理接口。例如PrinterPrx,在客户的地址空间中, PrinterPrx 的实例是“远地的服务器中的Printer接口的实例”的“本地大使”。与服务器端对象有关的全部细节,好比其地址、所用协议、对象标识,都封装在该实例中。
注意, PrinterPrx 继承自Ice.ObjectPrx。这反映了这样一个事实:全部的Ice 接口都隐式地继承自Ice::Object。
说的更明白些,就是这个类的方法调用都是远程服务端的调用,执行printString()方法的具体实现是在远程服务端执行的。
• <interface-name>PrxHelper.java 这个是接口的代理定义助手类,就是帮你得到代理类的。常常用的就两个方法checkedCast 和 uncheckedCast 。这两个方法实现的都是向下转换。
注意, checkedCast 会联系服务器。这是必要的,由于只有服务器状况中的代理实现确切地知道某个对象的类型。因此, checkedCast 可能会抛出ConnectTimeoutException 或ObjectNotExistException(这也解释了为什么须要助手类:ICE在运行时必须联系服务器,因此咱们不能使用Java 的向下转换)。
与此相反, uncheckedCast 不会联系服务器,而是会无条件地返回具备所请求的类型的代理 。可是,若是你要使用uncheckedCast,你必须肯定这个代理真的支持你想要转换到的类型;而若是你弄错了,你极可能会在调用代理上的操做时,引起运行时异常。对于这样的类型失配,最后可能会引起OperationNotExistException,但也有可能引起其余异常,好比整编异常。并且,若是对象碰巧有一个同名的操做,但参数类型不一样,则有可能根本不产生异常,你最后就会把调用发送给类型错误的对象;这个对象可能会作出很是糟糕的事情。
4)、创建一个ECLIPSE工程,将生成的文件拷贝到src目录下,并在classpath中导入Ice.jar。
5) 、创建三个测试JAVA文件,Server.java、PrinterI.java及Client.java:
PrinterI.java是对服务端实现骨架类_PrinterDisp的实现,返回时将PrinterI.java对象返回给客户端,这里实现的功能是直接输出传入的String参数:
public class PrinterI extends Demo._PrinterDisp {
public void printString(String s, Ice.Current current) {
System.out.println(s);
}
}
Server.java是服务端服务代理,用于接收客户端的请求操做:
public class Server {
public static void main(String[] args) {
int status = 0;
Ice.Communicator ic = null;
try {
//初使化链接,args能够传一些初使化参数,如链接超时时间,初使化客户链接池的数量等
ic = Ice.Util.initialize(args);
//建立名为SimplePrinterAdapter的适配器,并要求适配器使用缺省的协议(TCP/IP侦听端口为10000的请求)
Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000");
//实例化一个PrinterI对象,为Printer接口建立一个服务对象
Ice.Object object = new PrinterI();
//将服务单元增长到适配器中,并给服务对象指定名称为SimplePrinter,该名称用于惟一肯定一个服务单元
adapter.add(object, Ice.Util.stringToIdentity("SimplePrinter"));
//激活适配器,这样作的好处是能够等到全部资源就位后再触发
adapter.activate();
//让服务在退出以前,一直持续对请求的监听
ic.waitForShutdown();
} catch (Ice.LocalException e) {
e.printStackTrace();
status = 1;
} catch (Exception e) {
System.err.println(e.getMessage());
status = 1;
}
if (ic != null) {
// Clean up
//
try {
ic.destroy();
} catch (Exception e) {
System.err.println(e.getMessage());
status = 1;
}
}
System.exit(status);
}
}
Client.java是客户端代码,用于向服务端发起请求,并操做返回的代理对象:
public class Client {
public static void main(String[] args) {
int status = 0;
Ice.Communicator ic = null;
try {
//初使化
ic = Ice.Util.initialize(args);
//传入远程服务单元的名称、网络协议、IP及端口,获取Printer的远程代理,这里使用的stringToProxy方式
Ice.ObjectPrx base = ic.stringToProxy("SimplePrinter:default -p 10000");
//经过checkedCast向下转换,获取Printer接口的远程,并同时检测根据传入的名称获取的服务单元是否Printer的代理接口,若是不是则返回null对象
Demo.PrinterPrx printer = Demo.PrinterPrxHelper.checkedCast(base);
if (printer == null) throw new Error("Invalid proxy");
//把Hello World传给服务端,让服务端打印出来,由于这个方法最终会在服务端上执行
printer.printString("Hello World!");
} catch (Ice.LocalException e) {
e.printStackTrace();
status = 1;
} catch (Exception e) {
System.err.println(e.getMessage());
status = 1;
}
if (ic != null) {
// Clean up
//
try {
ic.destroy();
} catch (Exception e) {
System.err.println(e.getMessage());
status = 1;
}
}
System.exit(status);
}
}
6)、运行客户端和服务端
运行服务端:java Server
运行客户端:java Client
看看效果吧。
七、ICE的性能和效率
ICE的性能是比较好的,由于他自己的传输机制都是基于二进制,网上有人曾经作过性能测试,评价比较好,我本人尚未作性能测试,目前的判断只是基于网络数据,请先看下面的文章:
高性能计算-ICE 性能测试
ICE与CORBA比较的优点
八、ICE的优势
支持同步和异步的消息传递;
支持多个接口;
机器无关性,客户及服务器与底层的机器架构屏蔽开来。对于应用代码而言,像字节序和填充这样的问题都隐藏了起来;
语言无关性,客户和服务器能够分别部署,所用语言也能够不一样;
实现无关性,客户不知道服务器是怎样实现其对象的。这意味着,在客户部署以后,服务器的实现能够改变;
操做系统无关性,Ice API 彻底是可移植的,因此一样的源码可以在 Windows和 UNIX
上编译和运行;
线程支持,Ice run time 彻底是线程化的,其 API 是线程安全的,做为应用开发者,(除了在访问共享数据时进行同步)无需为开发线程化的高性能客户和服务器付出额外努力。
传输机制无关性,Ice 目前采用了TCP/IP 和 UDP做为传输协议。客户和服务器代码都不须要了解底层的传输机制;
位置和服务器透明性,Ice run time 会负责定位对象,并管理底层的传输机制,好比打开和关闭链接;
安全性,经过 SSL强加密,可使客户和服务器彻底安全地进行通讯,这样,应用可使用不安全的网络安全地进行通讯,你可使用 Glacier穿过防火墙,实现安全的请求转发,而且彻底支持回调;
内建的持久机制,使用 Freeze,建立持久的对象实现变成了一件很容易的事情,Ice提供了对高性能数据库 Berkeley DB[18] 的内建支持;
开放源码。
后记
这里只是简单的对ICE进行介绍,还有不少东西没有提到,如ICE的语法规则、ICE的版本控制(Facet)、持久化 (Feeze)、服务装箱管理 (ICEBox)、文件分发(ICEPatch2)、发布/订阅 服务(ICEStorm)、网络拓扑负载解决方案--终极武器(ICEGrid)、提供使用安全传输入协议SSL的插件(IceSSL)、轻量级的ICE应用防火墙其解决方案(Galcier2),这些留待你们后面去学习了。数据库