JavaRush /Java 博客 /Random-ZH /RMI:使用实践

RMI:使用实践

已在 Random-ZH 群组中发布
你好!今天我们来看看一个比较有趣的话题——RMI。这代表远程方法调用RMI:使用实践 - 1通过 RMI,您可以教两个程序相互通信,即使它们位于不同的计算机上。听起来很酷吗?:) 但这并不难做到!在今天的讲座中,我们将了解 RMI 交互由哪些部分组成以及如何配置它。我们首先需要一个客户端一个服务器。您不必太深入地了解计算机术语。对于 RMI,这只是两个程序。其中一个将包含某个对象,第二个将调用该对象的方法。调用一个程序中位于另一程序中的对象的方法 - 我们以前从未这样做过!是时候尝试一下了!:) 为了不被淹没在荒野中,让我们的程序变得简单。一般来说,服务器通常执行客户端请求的某种计算。对我们来说也是一样。我们将有一个简单的计算器程序作为服务器。她只有一个方法—— multiply()。它将客户端程序发送给它的两个数字相乘并返回结果。首先我们需要一个接口:
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Calculator extends Remote {

   int multiply(int x, int y) throws RemoteException;
}
为什么我们需要一个接口?事实上,RMI 的工作基于创建代理,您在之前的讲座中学习过这一点。您可能还记得,代理的工作是在接口级别而不是类级别上精确执行的。我们的界面有两个重要的要求!
  1. 它必须继承远程令牌接口。
  2. 它的所有方法都必须抛出 RemoteException(这不会在 IDE 中自动完成,您必须手动编写!)。
现在我们需要创建一个服务器类来实现我们的接口CalculatorRMI:使用实践 - 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);

   }
}
让我们弄清楚:) 在第一行中,我们创建某种字符串变量:
public static final String UNIQUE_BINDING_NAME = "server.calculator";
该字符串是远程对象的唯一名称。通过这个名称,客户端程序将能够找到我们的服务器:稍后您将看到这一点。接下来我们创建计算器对象:
final RemoteCalculationServer server = new RemoteCalculationServer();
这里一切都清楚了。下面这个就比较有趣了:
final Registry registry = LocateRegistry.createRegistry(2732);
这个叫做Registry的东西是已删除对象的注册表。“删除”并不是指我们从计算机中删除它们,而是事实上可以从其他程序远程访问该寄存器中的对象:)LocateRegistry.createRegistry()我们将数字 2732 传递给该方法。这是端口号。如果您不知道端口是什么,可以在这里阅读,但现在您只需要记住这是一个唯一的编号,其他程序可以通过它找到我们的对象注册表(您也将在下面看到这一点)。让我们继续。让我们看看下一行会发生什么:
Remote stub = UnicastRemoteObject.exportObject(server, 0);
在这一行我们创建一个存根存根将整个远程调用过程封装在其内部。可以说这是RMI最重要的元素。她在做什么?
  1. 接收有关远程调用方法的所有信息。
  2. 如果该方法有参数,存根会将它们反序列化。注意这一点!传递给远程调用方法的参数必须是可序列化的(毕竟,它们将通过网络传输)。我们没有这样的问题——我们只是传输数字。但如果您转移对象,请不要忘记它!
  3. 之后,它调用所需的方法。
我们将UnicastRemoteObject.exportObject()服务器计算器对象传递给该方法。这样我们就可以远程调用它的方法。我们只剩下一件事要做:
registry.bind(UNIQUE_BINDING_NAME, stub);
我们用一开始想出的名称在远程对象注册表中“注册”我们的存根。现在客户可以找到它了!你可能已经注意到,最后我们让程序的主线程进入睡眠状态:
Thread.sleep(Integer.MAX_VALUE);
我们只需要保持服务器长时间运行即可。我们将在 IDEa 中同时运行两个方法main():第一个是服务器方法(在我们已经编写的类中ServerMain),然后是客户端方法(在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);
我们很早就main()在类中启动了 该方法,现在是时候在客户端程序中启动该方法了! 控制台输出: 600 就是这样!我们的程序(甚至两个!)成功地履行了它的功能:)如果你有时间和愿望,你可以让它多样化一点。例如,确保计算器支持所有四种标准运算,并且不是数字,而是对象作为参数传递。作为附加材料,您可以在此处查看文章和示例: ServerMainmain()ClientMainCalculationInstance(int x, int y) 下一堂课见!:)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION