远程对象激活
----------------------------------------------------------------------
----------
第 7 章
主题:
概述
激活协议
“可激活”远程对象的实现模型
激活接口
.1 概述
分布式对象系统被设计为支持长期存在的持久对象。假设这些系统将由成千(也
许成万)个这样的对象组成,则对象的实现在无限期的时间段内被激活并保持活
动状态是不合理的。这将占用宝贵的系统资源。另外,客户机需要保存对对象的
持久引用的能力,这样在一个系统崩溃后可以重新建立对象之间的通讯,因为通
常对一个分布对象的引用只有当对象处于活动状态时才有效。
对象激活是一种用来提供对对象持久引用和管理对象实现的执行的机制。在 RMI
中,激活允许对象根据需要开始执行。当访问(通过方法调用)“可激活”远程
对象时,如果该远程对象当前尚未执行,则系统将在适当的 Java 虚拟机中开始
该对象的执行。
7.1.1 术语
活动 (active) 对象是在某些系统的 Java 虚拟机中被实例化并被导出的远程对
象。非活动对象在虚拟机中尚未被实例化(或导出),但能变成活动状态的远程
对象。激活就是将非活动对象转化为活动对象的过程。激活要求对象与一台虚拟
机相关联,而这将可能需要将该对象的类加载到虚拟机中,同时该对象也恢复其
持久状态(如果有)。
在 RMI 系统中,我们使用了惰性激活。惰性激活就是将一个对象的激活延迟到客
户机第一次使用该对象时(即第一次方法调用时)。
7.1.2 惰性激活
远程对象的惰性激活是用不完善远程引用(有时称为不完善块)实现的。对远程
对象的不完善远程引用在第一次调用对象的方法时,“完善”为活动对象的引用
。每个不完善引用均保持一个持久句柄(激活标识符)和对目标远程对象的瞬态
远程引用。远程对象的激活标识符包含足够的信息来使第三方激活该对象。瞬态
引用是对可用来联系执行对象的主动远程对象的“活动”引用。
在不完善引用中,如果对远程对象的活引用为空,则不认为目标对象是主动的。
在方法调用中,不完善引用(对该对象)将加入激活协议以获得“活”引用,该
引用是对新激活的对象的远程引用(例如单路传送 (unicast) 的远程引用)。一
旦不完善引用得到活引用,则不完善引用将把方法调用传给底层的远程引用,而
该远程引用又将方法调用传给远程对象。
具体的说,远程对象的 stub 包含一个“不完善”远程引用类型,该类型既包括:
远程对象的激活标识符,又包括
“活”引用(可能为空),其中包含远程对象的“活动”远程引用类型(例如,
带有单路传送语义的远程引用类型)。
----------------------------------------------------------------------
----------
注意 - RMI 系统对远程调用保留“至多一次”语义。换句话说,对可激活或单路
传送远程对象的调用将至多发送一次。因此,如果对远程对象的调用失败(由抛
出的 RemoteException 异常表示),则客户机将得到如下保证:远程方法的执行
不会超过一次,甚至根本就不执行。
7.2 激活协议
在远程方法调用期间,如果目标对象的“活”(live) 引用是未知的,则不完善引
用将使用激活协议。激活协议包括下列几个实体:不完善引用、激活器、Java 虚
拟机中的激活组和被激活的远程对象。
激活器(通常每个主机有一个)是一个实体,负责激活,它是:
将激活标识符映射到激活对象所需信息(对象的类、位置 -- URL 路径 -- 从该
处可加载类、对象可能需要用于自举 (bootstrap) 的特定数据等)的信息数据库,及
Java 虚拟机的管理器,它启动虚拟机(必要时)并将对象激活请求(和必要的信
息一起)传送到远程虚拟机中正确的激活组。
注意:激活器始终将激活标识符到活动对象的当前映射保存在缓存中,这样就无
需为每个激活请求而查询该组。
激活组(每个 Java 虚拟机中一个)是这样的实体,它接收对激活 Java 虚拟机
中对象的请求并将激活的对象返给激活器。
激活协议如下所示。不完善引用使用一个激活标识符并调用激活器(内部 RMI 接
口)来激活与该标识符关联的对象。激活器查找对象的激活描述符(先前已注册
)。对象的描述符包括:
对象的组标识符(指定对象激活时所处的虚拟机),
对象的类名,
URL 路径,从该处加载对象的类代码,
特定于对象的已编组的初始化数据(例如,初始化数据可能是包含对象持久状态
的文件的名称)。
如果应容纳该对象的激活组存在,则激活器将激活请求传送到该组。如果激活组
不存在,则激活器将启动虚拟机以执行激活组,然后将激活请求传送到该组。
激活组将加载对象的类并用特定的构造函数来实例化该对象。此构造函数带多个
参数,包括先前注册的激活描述符。
对象完成激活时,激活组将把已编组对象引用传回激活器,然后该激活器记录激
活标识符和激活引用对,并将活动(活)引用返给不完善引用。随后,不完善引
用(在 stub 内)通过活动引用将方法调用直接传给远程对象。
----------------------------------------------------------------------
----------
注意 - 在 JDK 中,RMI 提供激活系统接口的实现。要使用激活,必须首先运行
激活系统守护进程 (daemon) rmid。
7.3 “可激活”远程对象的实现模型
为了使可通过激活标识符访问的远程对象不受时间影响,开发人员必须做到:
为该远程对象注册一个激活描述符
在对象的类中包含一个专用构造函数,当 RMI 系统激活可激活对象时将调用它。
可用以下几种方法来注册激活描述符 (ActivationDesc):
调用类 Activatable 的静态 register 方法
用 Activatable 类的第一个或第二个构造函数创建“可激活”对象
显式地导出“可激活”对象。该过程可用 Activatable 的第一个或第二个 expo
rtObject 方法实现,其参数为 ActivationDesc、Remote 对象的实现和端口号。
对于特定对象,只可用上述三种方法之一来注册激活对象。有关如何实现可激活
对象的示例,请参阅后面的“构造可激活远程对象”。
7.3.1 ActivationDesc 类
ActivationDesc 含有激活对象所需的信息。它包含对象的激活组标识符、对象的
类名、加载对象代码的 codebase 路径(或 URL)及 MarshalledObject(可包含
每次激活期间所用的对象特定初始化数据)。
激活过程中将查询在激活系统中注册的描述符以获取有关的信息,从而用于重新
创建或激活对象。对象的描述符中的 MarshalledObject 将作为第二个参数传给
远程对象的构造函数,以供激活过程使用。
package java.rmi.activation;
public final class ActivationDesc implements java.io.Serializable
{
public ActivationDesc(String className, String codebase,
java.rmi.MarshalledObject data)
throws ActivationException;
public ActivationDesc(String className, String codebase,
java.rmi.MarshalledObject data,
boolean restart)
throws ActivationException;
public ActivationDesc(ActivationGroupID groupID,
String className,
String codebase,
java.rmi.MarshalledObject data,
boolean restart);
public ActivationDesc(ActivationGroupID groupID,
String className,
String codebase,
java.rmi.MarshalledObject data);
public ActivationGroupID getGroupID();
public String getClassName();
public String getLocation();
public java.rmi.MarshalledObject getData()
public boolean getRestartMode();
}
ActivationDesc 的第一个构造函数构造一个对象的对象描述符,这个对象的类是
className(可从 codebase 路径加载),它的初始化信息(已编组形式)为 d
ata。如果使用这种形式的构造函数,则对象的组标识符缺省为该虚拟机 Activa
tionGroup 的当前标识符。具有相同 ActivationGroupID 的所有对象都将在同一
虚拟机中被激活。如果当前组是非活动的或无法创建缺省组,则将抛出 Activat
ionException。如果 groupID 为 null,则将抛出 IllegalArgumentException。
----------------------------------------------------------------------
----------
注意 - 作为创建 ActivationDesc 的副效应,如果该虚拟机的 ActivationGrou
p 当前不是活动的,则将创建缺省 ActivationGroup。缺省激活组将 java.rmi.
RMISecurityManager 作为安全管理器,并在重新激活时将激活组虚拟机中的属性
设置为该虚拟机中的当前属性。如果应用程序需用不同的安全管理器,则在创建
缺省 ActivationDesc 之前必须设置该虚拟机的组。有关如何为虚拟机创建 Act
ivationGroup 的详细信息,参见方法 ActivationGroup.createGroup。
----------------------------------------------------------------------
----------
ActivationDesc 第二个构造函数构造对象描述符的方式与第一个构造函数相同,
但必须提供附加的参数 restart。如果对象要求重启服务,这意味着当激活器重
新启动时,对象也会自动重新启动(与根据需要激活的惰性激活相反)。此时,
restart 应为 true。如果 restart 为 false,则对象将只是在需要时激活(通
过远程方法调用)。
ActivationDesc 的第三个构造函数构造一个对象的对象描述符。这个对象的组标
识符为 groupID,它的类名为 className(可从 codebase 路径加载),它的初
始化信息为 data。所有具有相同 groupID 的对象都将在同一 Java 虚拟机中被
激活。
ActivationDesc 第四个构造函数构造对象描述符的方式与第三个构造函数相同,
但它允许指定重启模式。如果对象需要重新启动(定义如上),则 restart 应为
true。
getGroupID 方法返回该描述符所指定对象的组标识符。组可�