bboyjing's blog

尝试ZeorC ICE之【了解Ice概念和原理】

本章我们来稍微了解下Ice概念和原理,前面已经跑通过一个小例子,再回过来看概念和原理,可能会容易理解一些。作为一个复杂的RPC平台,Ice也创造了很多概念和术语,其中一个名词就是Slice,这个前面已经学习过了,但现在也只是会用,具体生成那一坨文件还没有细看。下面我们从几个基本概念来看下。

Ice Object

关于Ice Object,截取《ZeroC Ice权威指南》中的一句话:

拥有一个对象标识符Object Identity来区别与其他类型对象,Ice对象模型中要求对象标识符是全局唯一的,即没有任何对象的标识符相同。一个Ice Object在服务端具体化为一个Servant实例,,即我们用某种具体编程语言实现的一个Slice接口并新建的某个对象就是一个Servant。

看一段代码就清楚了,我们先来截取Server.java中的一段代码:

1
2
3
4
5
6
7
8
// 创建提供服务的站点,缺省协议为TCP/IP,端口为10000
Ice.ObjectAdapter adapter = ic.
createObjectAdapterWithEndpoints("MyServiceAdapter", "default -p 10000");
// 创建具体提供服务的实例
HelloWorldServiceImpl servant = new HelloWorldServiceImpl();
// 将服务实例添加到提供服务的站点
adapter.add(servant, Ice.Util.stringToIdentity("HelloService"));
adapter.activate();

再看ObjectAdapter的add()方法申明:Ice.ObjectPrx add(Ice.Object servant, Identity id);,可见第一个参数就是Ice.Object类型,Identity也能从第二个参数体现出来。这么看来,是否可以理解成HelloWorldServiceImpl就是一个Ice Object;而new HelloWorldServiceImpl()就是一个Servant实例?这个只是我个人的理解,不能确定。

ObjectAdapter

从Ice Object中引用的代码来看,Object Adapter像是Ice Object的宿主,提供了Ice Object的访问地址(Endpoint),并且负责完成请求处理转发的流程。下面来看下Object Adapter具有的功能:

  • 提供一个或多个通信端点(Endpoints),客户端通过这些断点中的某个端点,连接到一个具体的Ice Object对象,一个Endpoint由服务端所使用的通信协议、IP地址、端口等信息组成,例如default -h 192.168.0.1 -p 10000,表示采用默认协议(TCP),绑定在192.168.0.1的10000端口上。
  • 绑定一个或多个Servant,每个Servant与一个Ice Object映射,将客户端针对某个Ice Object的请求委派到对应的Servant上,并完成整个请求流程的处理过程,包括底层通信。从代码看就是adapter.add()方法实现的功能。

Ice Proxy

先引用下客户端调用的代码:

1
2
3
4
5
// 通过名称、协议、端口,构造通用Proxy对象
Ice.ObjectPrx base = ic.stringToProxy("HelloService:default -p 10000");
// 将通用Proxy对象转为真实的服务(uncheckedCast(base)可以减少一次远程调用)
HelloWorldServicePrx prxy = HelloWorldServicePrxHelper.checkedCast(base);
prxy.hello()

Ice Proxy是Ice Object在客户端的代表,简单来说,Proxy是客户端用来访问远程某个Ice Object的本地代理。Proxy存在于客户端的进程地址空间,当客户端调用远程对象的某个方法时,Ice运行时期的客户端代码库会完成如下工作:

  • 定位远程对象Ice Object
  • 如果Ice Object所在的Server处于关闭状态,则自动激活此Server,并激活远程对象。注:这一点暂时没有试出来
  • 将方法的入参通过Socket传输到远程对象
  • 等待调用完成
  • 将方法的出参返回给客户端,若发生异常则抛出调用异常

作为远程Ice Object的本地代理,Proxy还持有如下重要信息:

  • 远程Server的地址信息,用来初始化通信,可以通过ic.stringToProxy()方法初始化
  • 用来定位Ice Object的对象标识符:object identity,从代码来看该信息封装在checkedCast()方法中
  • 可选的Facet标识符,用来确定引用Ice Object的哪个接口
  • 一个具体的Proxy可以用一个包括Endpoint信息的字符串描述,比如HelloService:default -p 10000,表示在远端的TCP端口10000上绑定的一个HelloService对象的Proxy。

目前直观的感觉是一个Proxy对应一个Endpoint,也就是一个访问地址。Proxy有Direct Proxy与Indirect Proxy两种。前者是直接绑定某个远端Object的访问地址,就像上面引用的代码一样,这种情况下地址是写死的。后者则不绑定远程Object的某个具体通信地址,但是Endpoint的名称一定是有的,由于没有远程对象的地址信息,因此,需要借助寻址服务Location Service来获取对应的Ice Object的通信地址。如果要真的使用该产品的话,我想客户端肯定是采用Indirect Proxy这种方式来发现服务。

Location Service

Location Service其实就是利用了Ice的注册表来实现Object Identity到Endpoint的查询服务,通俗的讲也就是服务的发现。Ice的注册表中保持了Ice Object、Object Proxy、Server等相关信息,属于IceGrid体系的重要组成部分,这种注册表组件也是大多数分布式系统的关键组件之一。
理解Indirect Proxy对于理解和掌握Ice来说非常重要,因为在IceGrid这种分布式框架中,所有Proxy都是Indirect的,从而实现了复杂的负载均衡和故障恢复机制。其调用过程如下:
zeroc_ice_5

  • 客户端发起对远程Ice Object的方法InitialOp的调用
  • 客户端寻址,Indirect Proxy向Location Service发出查询命令,Location Service收到请求后从注册表Registry中获取对应Object的地址信息并返回
  • 客户端Ice Proxy直接跟远程对象建立通信连接,实现调用

总结

最后总结下上面学到的术语,并加以补充。在Ice中,Ice Object、Object Adapter、Servant属于服务端的概念,它们存在于一个Ice Server中,这个Server容器通常是一个IceBox进程,暂且可以理解为一个Tomcat。ASM(Active Servant Map)是一个对象标识符(Object Identity)到对应Servant的查询表,也就是Ice Object到Servant的映射表,当一个客户端在某个Endpoint上发起对某个Ice Object的请求访问时,ASM用来快速定位到具体的Servant上,以便高效地委派请求。下面给出一张示意图:
zeroc_ice_6
再看下Ice中另外一个重要概念–Replication,即让Object Adapter拥有多个访问地址,其目标是在多个机器上部署相同的Server来实现服务的负载均衡和容错机制,具体如何使用,我们后面再说。

本章节就到此结束,算初步了解了Ice的一些概念和原理,具体如何使用,后面将借助一些案例来学习。