John Squirrels
第 41 级
San Francisco

Java RMI

已在 随机的 群组中发布
你好!今天我们将考虑一个相当有趣的话题:Java RMI。这代表远程方法调用。您可以使用 RMI 允许两个程序相互通信,即使它们位于不同的计算机上。这听起来很酷吗?:) 而且做起来并不难!在今天的课程中,我们将分析 RMI 交互的元素并弄清楚如何配置它。我们首先需要的是客户端和服务器。我们真的不需要深入研究计算机术语。对于 RMI,这只是两个程序。其中一个将包含一个对象,另一个将调用该对象的方法。调用存在于不同程序中的对象的方法——这是我们尚未完成的事情!是时候尝试一下了!:) 为了避免陷入困境,让' 让我们的程序保持简单。通常,服务器执行客户端请求的一些计算。我们也会这样。我们的服务器将是一个简单的计算器程序。它只有一种方法:乘法()。它将客户端程序发送给它的两个数字相乘,然后返回结果。首先,我们需要一个接口:

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

public interface Calculator extends Remote {

   int multiply(int x, int y) throws RemoteException;
}
为什么我们需要接口?因为 RMI 依赖于创建代理,您在过去的课程中学习过。您可能还记得,我们通过接口而不是类来使用代理。我们的界面有两个重要的要求!
  1. 它必须扩展 Remote 接口。
  2. 它的所有方法都必须抛出 RemoteException(IDE 不会自动执行此操作——您需要手动添加!)。
现在我们需要创建一个服务器类来实现我们的计算器接口。 RMI 实践 - 2在这里,一切也很简单:

import java.rmi.RemoteException;

public class RemoteCalculationServer implements Calculator {

   @Override
   public int multiply(int x, int y) throws RemoteException {
       return x*y;
   }

}
这里真的没有什么可评论的:) 现在我们需要编写一个服务器程序来配置和运行我们的计算器对象。它看起来像这样:

import java.rmi.AlreadyBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class ServerMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, AlreadyBoundException, InterruptedException {

       final RemoteCalculationServer server = new RemoteCalculationServer();

       final Registry registry = LocateRegistry.createRegistry(2732);

       Remote stub = UnicastRemoteObject.exportObject(server, 0);
       registry.bind(UNIQUE_BINDING_NAME, stub);

       Thread.sleep(Integer.MAX_VALUE);

   }
}
让我们来解决这个问题 :) 在第一行中,我们声明了一些 String 变量:

public static final String UNIQUE_BINDING_NAME = "server.calculator";
该字符串是远程对象的唯一名称。我们的客户端程序使用这个名称来查找我们的服务器:您稍后会看到。接下来,我们创建我们的计算器对象:

final RemoteCalculationServer server = new RemoteCalculationServer();
这里一切都很清楚。接下来的事情更有趣:

final Registry registry = LocateRegistry.createRegistry(2732);
此注册表对象是远程对象的注册表。这些是其他程序可以远程访问的对象 :) 我们将数字 2732 传递给了LocateRegistry.createRegistry()方法。这是端口号 — 其他程序将使用它来查找我们的对象注册表的唯一编号(同样,您将在下面看到)。继续前进……让我们看看下一行会发生什么:

Remote stub = UnicastRemoteObject.exportObject(server, 0);
我们在这一行中创建一个存根。存根封装了整个远程调用。您可以认为这是 RMI 最重要的元素。它有什么作用?
  1. 它接收有关某个方法的远程调用的所有信息。
  2. 如果该方法有参数,存根将反序列化它们。注意这一点!您传递给远程调用方法的参数必须是可序列化的(毕竟,它们将通过网络传输)。这对我们来说不是问题——我们只是在传输数字。但是如果你正在传输对象,请不要忘记这个要求!
  3. 之后,存根调用所需的方法。
我们将计算器服务器对象传递给UnicastRemoteObject.exportObject()方法。这就是我们如何使远程调用它的方法成为可能。只剩下一件事要做:

registry.bind(UNIQUE_BINDING_NAME, stub);
我们在远程对象注册表中以我们一开始创建的名称“注册”我们的存根。现在客户将能够找到它!也许你注意到我们在最后让程序的主线程进入休眠状态:

Thread.sleep(Integer.MAX_VALUE);
我们只需要服务器长时间运行即可。在 IDE 中,我们会同时启动两个main()方法:首先是服务器的 main() 方法(在我们已经编写的ServerMain类中),其次是客户端的 main() 方法(在 ClientMain中,我们将在下面写)。当我们启动客户端时,服务器程序没有终止是很重要的,所以我们只是让它休眠了很长时间。无论如何,它将继续运行 :) 现在我们可以运行服务器的main()方法。让它运行并等待客户端程序调用一些方法:) 现在让我们编写客户端程序!它会将数字发送到我们的服务器进行乘法运算。

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class ClientMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, NotBoundException {

       final Registry registry = LocateRegistry.getRegistry(2732);

       Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);

       int multiplyResult = calculator.multiply(20, 30);

       System.out.println(multiplyResult);
   }
}
看起来很简单。但是这里发生了什么?首先,客户端必须知道它将远程调用其方法的对象的唯一名称。因此,在客户端程序中,我们创建了public static final String UNIQUE_BINDING_NAME = "server.calculator"; 多变的。 接下来,在main()方法中,我们可以访问远程对象的寄存器。为此,我们需要调用LocateRegistry.getRegistry()方法并传递用于在 ServerMain 程序中创建我们的注册表的端口号(端口 2732;此数字只是一个示例 - 您可以尝试使用不同的数字):

final Registry registry = LocateRegistry.getRegistry(2732);
现在我们只需要从注册表中获取所需的对象!这很容易,因为我们知道它的独特名称!

Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
注意类型转换。我们将接收到的对象转换为Calculator接口,而不是RemoteCalculationServer类。正如我们在课程开始时所说的那样,RMI 依赖于代理,因此远程调用只能用于接口的方法,不能用于类的方法。最后,我们远程调用对象的multiply()方法并将结果输出到控制台。

int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
ServerMain类的main ()方法已经运行了很长时间。现在是时候在客户端程序 ( ClientMain ) 中运行main()方法了!控制台输出:

600
就是这样!我们的程序(实际上是两个程序!)做了它应该做的 :) 如果你有时间和愿望,你可以稍微增加一点趣味。例如,使计算器支持四种标准算术运算,并且不是将数字作为原始类型传递,而是作为CalculationInstance(int x, int y)对象传递。下节课见!:)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION