RMI
Java RMI(Remote Method Invocation)
简介
Java RMI 机制可以获取一个远程主机(JVM)上的对象的引用,使用这个对象时就和它在本地的主机一样。
RMI 可以调用远程对象的方法,传递对象作为参数,同时可以从返回值中获取对象。
RMI 使用对象序列化,有时也使用动态类加载和 security manager 来安全地传输 Java 类。
远程对象和非远程对象
远程对象和普通的对象一样通过引用传递,方法调用发生在原来的主机中的原来的对象上。远程主机返回一个对象的引用后,可以通过这个引用调用方法。
非远程对象只是一般的可序列化的对象,它是按值传递的(传递时得到的是原来的复制)。
Stubs and Skeletons
调用远程对象方法时,实际上是在调用本地的一串代码,作为远程对象的代理,称为 Stub。
Skeleton 在远程服务器上,是真正的对象的代理,接受来自远程的方法调用并传递给真正被调用的对象。
远程对象的 Stub 和 Skeleton 通过运行 rmic
(RMI Compiler)创建。当编译完成后,在远程对象的类中运行rmic
。
Remote Interfaces
远程对象需要实现远程接口,这个远程接口继承自 java.rmi.Remote
,完成后一个 Stub 就能够自动创建。然后需要引用远程对象作为远程接口的一个实例而不是类的实例。因为真正的对象和 Stub 都实现了远程接口,对于方法调用来说,它们是一样的类型。
远程对象的 public 字段无法直接访问,需要通过访问器函数。
远程接口的方法必须声明为可抛出java.rmi.RemoteException
异常。这个异常当任何网络错误发生时会抛出。
下面是一个远程接口,描述了远程对象的动作,含有两个可远程调用的方法,返回 Widget
对象
1 | import java.rmi.* |
UnicastRemoteObject 类
远程对象(实现了远程接口的)通常会继承 java.rmi.server.UnicastRemoteObject
类。创建这个类的子类时,RMI 运行时会自动开始监听来自远程接口(Stub)的网络链接。
以下是一个实现了RemoteObject
接口的远程对象
1 | public class MyRemoteObject implements RemoteObject extends java.rmi.UnicastRemoteObject |
除了继承 UnicastRemoteObject,也可以使用它的静态方法exportObject
导出一个类。
通常,导出的对象短暂监听一个端口,通过使用UnicastRemoteObject.exportObject
的另一个重载可以指定明确的端口。
RMI registry
RMI registry 用于查找所需的远程对象。它由Naming
类和rmiregistry
程序组成。rmiregistry
需要在使用它的 Java 程序启动前在本地运行。
当一个客户端对象需要寻找服务器上的对象时,它会请求一个 URL,包含 rmi 协议,主机名和需要请求的对象名。Naming
类与 RMI registry 交互并返回远程对象的引用。
RMI 调用过程
传输的对象必须实现java.io.Serializable
接口,并且客户端和服务端的serialVersionUID
要一致。
Server 开放两个服务:RMI Registry(默认为 1099 端口),远程对象的通信端口(随机)
有时 RMI Registry 不在 Server 端上而是在另一台服务器,区别不大。
远程调用方法的过程:
- Server 监听一个端口(远程对象的通信端口,随机)
- 获取 Stub,Stub 中包含远程对象的地址和端口等信息
- Client 调用 Stub 的方法
- Stub 连接到 Server 端监听的通信端口,提交参数
- Server 端执行具体方法,返回给 Stub
- Stub 将结果返回给 Client
获取 Stub 的过程:
- Server 的 Remote Object 向 RMI Registry 注册远程对象名称
- Client 通过名称向 RMI Registry 查询远程对象
- RMI registry 发送 Stub 给 Client
- Client 通过 Stub,Stub 通过 Skeleton 向远程对象发起调用