作用
在运行期间,ice框架为了完全隔绝通信层对于客户业务服务的影响,将通信层用到的所有资源都与客户业务服务用到的资源完全隔离开来了。为了完成这个目标,写代码进行封装的时候,我们一般封装一个资源管理类来对通信层所用的资源进行管理,ice则用communicator来实现这个功能。
另一方面,框架为了完全独立,一般都有个全局的入口点,类似于程序的入口main函数。ice框架则用Ice::Communicator 来代表运行期间框架的主进入点。
管理的资源
-
客户端线程池
客户端线程池会确保,在客户端,至少有一个线程可用于接收对未完成请求的答复。这确保了不会发生死锁。例如,如果服务器在操作实现中回调客户,即使客户正在等待同一服务器答复它的请求,客户端接收者线程也能处理来自服务器的请求。客户端线程池还被用于异步方法调用(AMI),用以避免在回调中发生死锁。 -
服务器端线程池
这个线程池里面的线程负责接受到来的连接,并处理来自客户的请求。 -
配置属性
Ice run time 的各个方面可以通过属性进行配置。每个通信器都有自己的配置属性集。 -
对象工厂
为了实例化从已知基类派生的类,通信器维护有一组对象工厂,能够替Ice run time 对类进行实例化。 -
一个日志记录器对象
日志记录器对象实现了Ice::Logger 接口,并负责确定Ice run time产生的日志消息的处理方式。 -
一个统计对象
统计对象实现了Ice::Stats 接口,通信器会把通信流量(发送和接收字节数)告知该对象。 -
一个缺省路由器
路由器实现了Ice::Router 接口。Glacier 使用了路由器来实现Ice 的防火墙功能功能。 -
一个缺省定位器
定位器是用于把对象标识解析为代理的对象。定位器对象被用于构建定位服务,比如IcePack 。 -
一个插件管理器
插件是用于给通信器增加特性的对象。例如, IceSSL被实现成插件。每个通信器都有一个插件管理器,这个管理器实现Ice::PluginManager 接口,通过它,你可以访问通信器的插件集。 -
对象适配器
对象适配器负责分派到来的请求,并把每个请求传给正确的servant。使用不同通信器的对象适配器和对象完全是相互独立的。特别地:- 每个通信器都使用自己的线程池。例如,这意味着,如果一个通信器耗尽了用于处理到来请求的线程,只有使用该通信器的对象会受影响。使用其他通信器的对象有自己的线程池,因而不受影响。
- 跨越不同通信器的并置调用不会被优化,而使用同一通信器的并置调用则能避免产生大部分调用分派开销。服务器通常只使用一个通信器,但有时多个通信器也会很有用。例如,加载的每个Ice 服务使用了单独的通信器,以确保不同的服务不会相互干扰。多个通信器器还能用于避免线程饥饿:如果一个
服务耗尽线程,其他的服务不会受影响。
通信器的接口
通信器的接口是用Slice 定义的。下面是其部分接口:
module Ice
{
local interface Communicator
{
string proxyToString(Object* obj);
Object* stringToProxy(string str);
ObjectAdapter createObjectAdapter(string name);
ObjectAdapter createObjectAdapterWithEndpoints(string name,string endpoints);
void shutdown();
void waitForShutdown();
void destroy();
// ...
};
// ...
};
通信器提供了一些操作:
proxyToString
stringToProxy
这两个操作允许你把代理转换成串化表示,或进行反向转换。
createObjectAdapter
createObjectAdapterWithEndpoints
这两个操作创建新的对象适配器。每个对象适配器都与一个或更多传输端点关联在一起。一个对象适配器通常拥有一个传输端点。但是,一个对象适配器也可以提供多个传输端点。如果是这样,这些端点可以通往同一组对象,但代表访问这些对象的不同手段。这很有用,例如,服务器在防火墙后面,但必须让内部和外部的客户都能访问它;把适配器同时绑定到内部和外部接口,就可以其中任何一个接口访问在服务器中实现的对象了。
createObjectAdapter 根据配置信息来确定把自己绑定到哪个端点,而createObjectAdapterWithEndpoints 允许你为新适配器指定传输端点。你通常会优先于createObjectAdapterWithEndpoints使用createObjectAdapter。这样,就能把与传输机制相关的信息(比如主机名和端口号)放在 源码外面,通过改变属性,你可以对应用进行重配置(于是,在传输端点需要改变时,不用重新进行编译)。
shutdown
这个操作关闭服务器端的Ice run time:
- 在shutdown 被调用时,仍处在执行过程中的操作调用可以正常完成。shutdown 不会等待这些操作完成;在shutdown 返回时,你所知道的是:不会再有新的请求被分派,但在你调用shutdown 时已经在执行之中的操作可能仍在运行。你可以调用waitForShutdown,等待仍在执行的操作完成。
- 在服务器调用shutdown 之后到达的操作调用或者会失败(抛出ConnectFailedException),或者会被透明地重定向到服务器的某个新实例。
waitForShutdown
这个操作挂起发出调用的线程,直到通信器关闭为止(也就是说,直到在服务器中不再有操作在执行为止)。这样,你可以在销毁通信器之前,等待服务器空闲下来。
destroy
这个操作销毁通信器及其相关资源,比如线程、通信端点,以及内存资源。一旦你销毁了通信器(因此也就销毁了该通信器的runtime),你不能再调用其他任何Ice 操作(除非创建另外的通信器)。
在离开程序的main 函数之前要调用destroy,不这样做会导致不确定的行为。
在离开main 之前调用destroy 是必需的,因为destroy 会在返回之前等待所有运行中的线程终止。如果你没有调用destroy 就离开main,你就会留下许多仍在运行的线程;许多线程包不允许你这样做,你最终会使程序崩溃。
如果你没有调用shutdown 就调用destroy,在这个调用返回之前,它会等待所有执行中的操作调用完成(也就是说, destroy 的实现隐含地调用shutdown,然后调用waitForShutdown)。shutdown (因此,也包括destroy)会解除与通信器相关联的所有对象适配器的激活状态。Since destroy blocks until all operation invocations complete, a servant will deadlock if it invokes destroy on its own communicator while executing a dispatched operation.
在客户端,如果你在操作执行过程中调用shutdown,这些操作会终止,抛出CommunicatorDestroyedException。