什么是ICE(Internet Communications Engine)呢,它是由Zeroc公司开 发的一套开源中间件系统,与DCOM,CORBA,WEB SERVICEDcom类似,支持RPC(Remote Procedure Call 远程过程调用)协议,但是在效率上却高于前面所述几种技术方案,是一种适用于各种环境的面向对象的中间件平台:客户和服务器可以用不同的编程语言来编写, 可以运行在不同的操作系统和系统架构上,并且可以使用多种网络技术经行通信(例如TCP、UDP、SSL等),这给我们的应用和部署带来很大的便捷性。
ICE的优点
1、跨平台,支持多种语言
2、面向对象编程
3、为分布式应用方面提供了一整套 强大的特性和功能支持(例如负载平衡、软件分发、数据同步等)。
4、在网络带宽、内存使用和CPU开销方面已经内置了高效的实现。
5、内置了安全特征的实现,可跨越不安全的网络(广域网)使用。
6、降低了复杂性,易于学习和使用。
与WebService的对比
说道远程调用,我们最熟悉的莫过于WebService了,那么就对他们做一下简单的对比,其 中星的颗数有待商榷,表达的也仅是一种意思,不过从我个人的角度上来看,他们已经没有可比性了,因为你拿的是一台机器和一群机器在比。
在我看来,ICE的强大在于它推出了ICEGrid,在 后续的文章中我将会重点介绍它。
一些基础的概念和模型
首先我们来看看ICE的基础调用模型(如下 图所示),值得一提的是在单项调用和数据报业务中,4、5、6几步是没有的,这里面的几个概念和关键词特别重要,也是我们后面学习的基础
Client(客 户端):估计这个大家都非常清楚,通俗的讲就是一个请求的发起方。
Proxy(代 理):代理实际上就是远程服务驻在本地的一个代表,创建 它时首先会和远程服务经行握手和状态确认等操作,Client所有的操作都是从过Proxy来办理的。代理又分为直接代理(已经知道服务端的位置及其他信 息)和间接代理(不知道服务器在哪里,由Registry注册器告诉它地址等信息)。
Adapter(适配器):是配置相当于服务单位 (Servant)的管理者,它管理着每个请求该分配给哪一个服务单位。
Servant(服务单元):它是服务的最小单位,一般是具体到 某个接口的实现。
刚 才我们提到了接口的实现,但是这个接口是谁定义的,这个时候我们不免要提到Slice(Specification Language for Ice),也就是ICE所使用的“语言”,正是有了这个“中间语言”,我们才可以做到支持各种编程语言,因为你所使用的语言只要跟Slice打交道就可以 了,关于Slice我在后面可能会详细的来讲述。
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的版本控制(Facet)
ICE内部集成非常好的版 本 控制功能(Facet),在每一个服务单元(Servant)其实都有一个Facet与之并存,如果你没有认为指定,那么这个默认Facet的值就为空 (String.Empty)。在我们业务的实际应用过程中,经常要碰到服务升级的情况,但是升级服务就意味着增大了原有业务调用的稳定性,从而增大了风 险,这个Facet就刚好解决我们遇到的这个难题,我们每次升级都是“新增服务”,而非改变原有业务,新来的业务调用新版本服务,原有业务调用原有版本服 务。当检测一些很“老”的服务确认无人调用的时候我们就可以关掉了。
Facet的实现其实也非常简单,服务端(Adapter)在挂载服 务(Servant)的时候就制订了相应版本号。这时候客户端要调用此方法就必须提供正确的版本号(而非默认版本号)。
持久化 (Feeze)
一 款对Servant服务进行管理和持久化的工具,Adapter可以注册多个Servant在内存中,当 Adapter接收到一个新的请求(request)后便去管理列表里面查找是否存在已有的Servant,如果存在则返回该Servant,如果不存在 则实例化一个新的Servant实例。当不停的实现新Servant之后势必会造成内存的激增已经CPU资源的浪费,而且一些很少被调用的Servant 也一直驻存在服务中。为此我们设定服务的Servant的上限,到达该上限后自动将“最少调用”的Servant状态保存在数据库(或文件等持久化设备) 中,然后关闭这些Servant,一旦有新的请求此Servant时再次获取原状态来激活Servant……
服务装箱管理 (ICEBox)
从 名字就可以看出来,它是一款装箱托管方案,类似于IIS,可以托管多个应用服务,并且每个服务是支持插拔式管理的, 相互之间不受影响,对ICE的应用做了接口设计,每个服务只是实现了 Ice.Services接口就可以了,这样一样让我们可以更专注的关于于我们的业务而非运行的平台。它为我们提供了统一的托管平台,是一个很不错的工 具。
文件分发(ICEPatch2)
ICE最重要的功能就是多节点部署,以达到负载平衡或者容灾的目的,大多数时候 每个节点(一般而言是一台服务器)配置的应用程序都是一样的,当每次有更新或者新增应用的时候,便需要手工同步每个节点的文件,这样的手工操作显得十分繁 琐而且完全依赖操作人的行为。而ICEPatch2解决了这一难题,只要有一台以为的ICEPatch2服务在运行当中,就可以通过分发的方式同步其他各 个节点的文件,也就是说运维人员只需要维护一台机器的应用程序文件就可以了。
发布/订阅 服务(ICEStorm)
未 完待续……
网络拓扑负载解决方案(ICEGrid) --终极武器
icegrid实际上是一系列组件的组合,形成了 一套强大的 文件分发、负载均衡、快捷部署等解决方案。这也是ICE功能中最为强大的一套,至于细节,有待后文慢慢道来。
学习ICE,我们要从ICE的“语言”学起,简称SLICE(Simple Language Of ICE)。SLICE有自己独立的语法体系和结构,我们必须必须熟悉和遵守这些语法体系才能到达我们想要的效果。
按照官方的说明文档,结合自己的使用,我们可以以下几块来讲解:
- 语法规范
- 基本类型
- 用户自定义类型
- 接口及其异常
- 生成代码分析
- 文档化
一、语法规范
像其他语言一样,SLICE也有自己的语法规范,包括 命名、格式、关键词、敏感前后缀等。同样我们也可以借鉴学习其他语言的方法来学习它,入门阶段我们并不需要掌握每一个关键词和命名规则等,掌握几个常用的 关键词就可以了,如下:
module : 定义命名空间或者包路径
interface:接口
struct:结构(编译成C#后为Class)
Class:类
exception:异常
throws:抛出异常
sequence:序列
dictionary:字典
idempotent:等幂方法(即调用多次不影响最终结果)
这些关键词基本上组成了ICE语法的骨干,除此之外,还有一个重要的功能就是属性Attribute。
Attribute 可以为我们做很多事情,大致可以分为两类,一类是是ICE的固有特性,如ami,amd,deprected等,表名具有ICE 某种特定的功能。另一类则是特定语言(如Java、Net等)所有用的特征,如clr:generic:List、clr:generic:Queue等 (.net),标记上这些属性后在生成相应的cs文件时则会生成相应的类型。具体功能可以参见手册的附录B(3.4.0)
掌握了这些“语法”之后,我们基本上就可以来组织内容了,不过从实用角度上来,还有一些值得注意的地方:
- 每个语句的定义后面都要以分号“;”来结束,包括命名空间的定义。
- 除了注释以外,不能包含ASCII码以外的任何字符(例如中文)。
- ICE里面的任何方法、接口、类等都是对象,而且讲究对象之间不能有重复命名,所以我们在定义的时候不能有重复(这方面是忽略大小写的,例如 name与Name是一样的)。
- ICE文件是可以复用的,类似C语法,在文件头处加上#include即可。
- 如果确实要用到某些关键词来命名,则可以用转义字符“\”来标识,例如 “\dictionary”。
掌握了以上几点,我们来做个例子(以下代码不包含任何功能定义):
module joyaspx{
module com{
/* Code here*/
};
};
二、基本类型
了解以上基本语法之后,我们还需掌握一些基本类型。何谓基本类型,基本类型就是可以解析成为所有语言(C++、Java、.NET等)都能识别的类 型,这些基本类型类型可以总结为两种:字符型和数字型。详细见下表(摘自手册):
看完以上表格,细心的朋友马上就会发现一个问题,常见的Decimal和DateTime类型怎么没有在列表中看到呢。经过官方查证,确实不存在这 两种常用类型,这对于熟悉特定语言(比如.net)的朋友来说确实存在一些不方便之处。既然已成为事实,那么我们就来讨论如果解决此问题。
对于Decimal类型,如果为一般的企业,大部分的情况下都可以用double来替代,关系不是很大;但是对于DateTime类型就没有这么乐 观了,不过幸运的是DateTime有个Ticks属性,该属性类型为long,也就是说还是可以和long类型互转的,如下所示:
long ticks = DateTime.Now.Ticks;
DateTime now = new DateTime(ticks);
module joyaspx{
module com{
class person{
string name;
int age;
bool gender;
};
};
};
然后使用slice2cs工具生成相应的cs代码,我们来看看生成了什 么。
相应生成语句 :slice2cs *.ice (slice2cs工具后面将会介绍)
- 构造函数 。构造函数有两个重载,一个是无参数的构造函数,另一个是带参数的构造函数,参数分别是各个字段(属性)的赋值。
-
Ice.Object基类方法实现。
这里共有7个方法,分别是ice_isA、ice_ids、ice_id和ice_staticid四类方法,另外三个方法分别是带有当前上下文的重载。 这四类方法从名字就可以大致看出其中的含义:ice_isA是判断当前对象是否与指定类相符,ice_ids是获取当前对象的类属性(包括父类继承,继承 关系直到基类为止),ice_id是获取当前对象的类型标识符(TypeId),ice_staticid与ice_id功能相同,只不过为静态方法。 详细可以参见手册4.14一节
- 元数据支持
三、接口、异常
这一章可能是我们最为关心的一种那个,前面的语法与基本类最终都是为了实现某个接口而服务的,接口即意味了与外界进行的通讯的渠道了,有了前面的基 础,我们定义一个接口也容易多了。