RMI(远程方法调用)

17 Mar 2025 | 6 分钟阅读

RMI(远程方法调用)是一个 API,它提供了一种在 Java 中创建分布式应用程序的机制。RMI 允许一个对象调用在另一个 JVM 中运行的对象的​​方法。

RMI 使用两个对象存根 (stub)骨架 (skeleton)在应用程序之间进行远程通信。

理解存根 (stub) 和骨架 (skeleton)

RMI 使用存根和骨架对象与远程对象进行通信。

一个远程对象是一个对象,其方法可以从另一个 JVM 调用。 让我们来理解一下存根和骨架对象

存根 (stub)

存根 (stub) 是一个对象,充当客户端的网关。所有传出的请求都通过它路由。它位于客户端,代表远程对象。当调用者在存根对象上调用方法时,它会执行以下任务

  1. 它启动与远程虚拟机 (JVM) 的连接,
  2. 它将参数写入并传输(编组)到远程虚拟机 (JVM),
  3. 它等待结果
  4. 它读取(解组)返回值或异常,并且
  5. 最后,它将值返回给调用者。

骨架 (skeleton)

骨架 (skeleton) 是一个对象,充当服务器端对象的网关。所有传入的请求都通过它路由。当骨架收到传入的请求时,它会执行以下任务

  1. 它读取远程方法的参数
  2. 它调用实际远程对象的方法,并且
  3. 它将结果写入并传输(编组)给调用者。
在 Java 2 SDK 中,引入了一种存根协议,它消除了对骨架的需求。 RMI 中的存根和骨架

理解分布式应用程序的要求

如果任何应用程序执行这些任务,它就可以成为分布式应用程序。

.
  1. 应用程序需要定位远程方法
  2. 它需要提供与远程对象的通信,并且
  3. 应用程序需要加载对象的类定义。

RMI 应用程序具有所有这些功能,因此它被称为分布式应用程序。


Java RMI 示例

给出了编写 RMI 程序的 6 个步骤。

  1. 创建远程接口
  2. 提供远程接口的实现
  3. 编译实现类并使用 rmic 工具创建存根和骨架对象
  4. 通过 rmiregistry 工具启动注册服务
  5. 创建并启动远程应用程序
  6. 创建并启动客户端应用程序

RMI 示例

在此示例中,我们遵循了所有 6 个步骤来创建和运行 rmi 应用程序。客户端应用程序只需要两个文件,即远程接口和客户端应用程序。在 rmi 应用程序中,客户端和服务器都与远程接口交互。客户端应用程序调用代理对象上的方法,RMI 将请求发送到远程 JVM。返回值被发送回代理对象,然后发送到客户端应用程序。

RMI example

1) 创建远程接口

为了创建远程接口,扩展 Remote 接口并使用远程接口的所有方法声明 RemoteException。在这里,我们正在创建一个扩展 Remote 接口的远程接口。只有一个名为 add() 的方法,它声明了 RemoteException。


2) 提供远程接口的实现

现在提供远程接口的实现。为了提供远程接口的实现,我们需要

  • 扩展 UnicastRemoteObject 类,
  • 或使用 UnicastRemoteObject 类的 exportObject() 方法
如果扩展 UnicastRemoteObject 类,则必须定义一个声明 RemoteException 的构造函数。

3) 使用 rmic 工具创建存根和骨架对象。

下一步是使用 rmi 编译器创建存根和骨架对象。 rmic 工具调用 RMI 编译器并创建存根和骨架对象。


4) 通过 rmiregistry 工具启动注册服务

现在使用 rmiregistry 工具启动注册服务。 如果您没有指定端口号,它将使用默认端口号。 在此示例中,我们使用端口号 5000。


5) 创建并运行服务器应用程序

现在 rmi 服务需要在服务器进程中托管。 Naming 类提供了获取和存储远程对象的方法。 Naming 类提供了 5 种方法。

public static java.rmi.Remote lookup(java.lang.String) throws java.rmi.NotBoundException, java.net.MalformedURLException, java.rmi.RemoteException;它返回远程对象的引用。
public static void bind(java.lang.String, java.rmi.Remote) throws java.rmi.AlreadyBoundException, java.net.MalformedURLException, java.rmi.RemoteException;它将远程对象绑定到给定的名称。
public static void unbind(java.lang.String) throws java.rmi.RemoteException, java.rmi.NotBoundException, java.net.MalformedURLException;它销毁与给定名称绑定的远程对象。
public static void rebind(java.lang.String, java.rmi.Remote) throws java.rmi.RemoteException, java.net.MalformedURLException;它将远程对象绑定到新名称。
public static java.lang.String[] list(java.lang.String) throws java.rmi.RemoteException, java.net.MalformedURLException;它返回注册表中绑定的远程对象的名称数组。

在此示例中,我们正在通过名称 sonoo 绑定远程对象。


6) 创建并运行客户端应用程序

在客户端,我们通过 Naming 类的 lookup() 方法获取存根对象,并调用此对象上的方法。在此示例中,我们在同一台机器上运行服务器和客户端应用程序,因此我们使用 localhost。如果要从另一台机器访问远程对象,请将 localhost 更改为远程对象所在的主机名(或 IP 地址)。



此 RMI 示例的输出

RMI RMI

带有数据库的 RMI 应用程序的实用示例

考虑一种情况,即两个应用程序在不同的机器上运行。 假设 MachineA 和 MachineB,MachineA 位于美国,MachineB 位于印度。 MachineB 想获取 MachineA 应用程序的所有客户列表。

让我们按照以下步骤开发 RMI 应用程序。

1) 创建表

首先,我们需要在数据库中创建表。 在这里,我们使用 Oracle10 数据库。

RMI application with database

2) 创建 Customer 类和远程接口

文件:Customer.java

注意:Customer 类必须是可序列化的。

文件:Bank.java

3) 创建提供远程接口实现的类

文件:BankImpl.java

4) 编译类 rmic 工具并通过 rmiregistry 工具启动注册服务

RMI example with database

5) 创建并运行服务器

文件:MyServer.java
example of remote method invocation

6) 创建并运行客户端

文件:MyClient.java
real world example of rmi