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
2
3
4
5
6
import java.rmi.*

public interface RemoteObject extends Remote {
public Widget doSomething() throws RemoteException;
public Widget doSomethingElse() throws RemoteException;
}

UnicastRemoteObject 类

远程对象(实现了远程接口的)通常会继承 java.rmi.server.UnicastRemoteObject类。创建这个类的子类时,RMI 运行时会自动开始监听来自远程接口(Stub)的网络链接。

以下是一个实现了RemoteObject接口的远程对象

1
2
3
4
5
6
public class MyRemoteObject implements RemoteObject extends java.rmi.UnicastRemoteObject
{
public RemoteObjectImpl() throws RemoteException {...}
public Widget doSomething() throws RemoteException {...}
public Wdiget doSomethingElse() throws RemoteException {...}
}

除了继承 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 端上而是在另一台服务器,区别不大。

远程调用方法的过程:

  1. Server 监听一个端口(远程对象的通信端口,随机)
  2. 获取 Stub,Stub 中包含远程对象的地址和端口等信息
  3. Client 调用 Stub 的方法
  4. Stub 连接到 Server 端监听的通信端口,提交参数
  5. Server 端执行具体方法,返回给 Stub
  6. Stub 将结果返回给 Client

获取 Stub 的过程:

  1. Server 的 Remote Object 向 RMI Registry 注册远程对象名称
  2. Client 通过名称向 RMI Registry 查询远程对象
  3. RMI registry 发送 Stub 给 Client
  4. Client 通过 Stub,Stub 通过 Skeleton 向远程对象发起调用

RMI 利用

作者

lll

发布于

2021-04-14

更新于

2022-09-19

许可协议