设计远程interface

计算引擎的核心是一个协议,该协议使任务可以提交到计算引擎,计算引擎可以运行这些任务,并将这些任务的结果返回给 Client 端。该协议在计算引擎支持的interface中表示。下图说明了此协议的远程通信。

Client 端与计算引擎之间的远程通信

每个interface都包含一个方法。计算引擎的远程interfaceCompute使任务可以提交到引擎。Client 端interfaceTask,定义计算引擎如何执行提交的任务。

compute.Computeinterface定义了可远程访问的部分,即计算引擎本身。以下是Computeinterface的源代码:

package compute;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Compute extends Remote {
    <T> T executeTask(Task<T> t) throws RemoteException;
}

通过扩展interfacejava.rmi.RemoteComputeinterface将自身标识为可以从另一个 Java 虚拟机调用其方法的interface。实现此interface的任何对象都可以是远程对象。

作为远程interface的成员,executeTask方法是一种远程方法。因此,必须将此方法定义为能够抛出java.rmi.RemoteException。 RMI 系统从远程方法调用中抛出此异常,以表明发生通信故障或协议错误。 RemoteException是已检查的异常,因此任何调用远程方法的代码都需要通过catch该异常或在其throws子句中对其进行声明来处理该异常。

计算引擎所需的第二个interface是Taskinterface,它是Computeinterface中executeTask方法的参数类型。 compute.Taskinterface定义计算引擎与其需要执行的工作之间的interface,从而提供了开始工作的方式。这是Taskinterface的源代码:

package compute;

public interface Task<T> {
    T execute();
}

Taskinterface定义单个方法execute,该方法不带参数,也不引发异常。因为该interface未扩展Remote,所以该interface中的方法不需要在其throws子句中列出java.rmi.RemoteException

Taskinterface具有类型参数T,它表示任务计算的结果类型。该interface的execute方法返回计算结果,因此其返回类型为T

Computeinterface的executeTask方法又返回传递给它的Task实例的执行结果。因此,executeTask方法具有其自己的类型参数T,该参数将其自身的返回类型与所传递的Task实例的结果类型相关联。

RMI 使用 Java 对象序列化机制在 Java 虚拟机之间按值传输对象。对于被视为可序列化的对象,其类必须实现java.io.Serializable标记interface。因此,实现Taskinterface的类也必须实现Serializable,用于任务结果的对象的类也必须实现。

Compute对象可以执行各种任务,只要它们是Task类型的实现即可。实现此interface的类可以包含任务计算所需的任何数据以及计算所需的任何其他方法。

这就是 RMI 如何使这种简单的计算引擎成为可能。因为 RMI 可以假定Task对象是用 Java 编程语言编写的,所以 RMI 可以根据需要将以前对计算引擎未知的Task对象的实现下载到计算引擎的 Java 虚拟机中。此功能使计算引擎的 Client 端可以定义要在服务器计算机上运行的新任务,而无需在该计算机上显式安装代码。

ComputeEngine类实现的计算引擎实现了Computeinterface,从而可以通过调用其executeTask方法将不同的任务提交给它。使用execute方法的任务实现来运行这些任务,并将结果返回给远程 Client 端。