RMI 代表远程方法调用,顾名思义,它是Java程序调用在另一台计算机上运行的对象的方法的协议。它提供了一个API(应用程序编程接口),用于从一个程序(称为服务器)中导出对象并从可能在另一台计算机上运行的另一个程序(称为客户端)中调用该对象的方法。
Java RMI注册表是Java RMI系统的关键组件,它为服务器注册服务和客户端查找这些服务提供了集中目录。在本文中,我们将学习如何实现服务器以暴露对象,并实现客户端以调用服务器上的方法,以及如何在RMI注册表中注册和查找服务。
服务器接口
要了解Java RMI系统如何工作的复杂性,让我们实现一个简单的服务器对象,该对象提供一种接受名称并返回问候语的方法。这是对象接口的定义:
import java.rmi.Remote;import java.rmi.RemoteException;public interface Greeting extends Remote{ public String greet(String name) throws RemoteException;}
接口的名称称为问候。它提供了一个称为 greet()的方法,该方法接受名称并返回合适的问候语。
要将此接口标记为可导出,需要扩展 java。 rmi.Remote 界面。该方法还需要声明一个 throws 子句,其中列出了 java.rmi.RemoteException ,以及任何特定于应用程序的异常。这样,客户端代码可以处理(或传播)远程方法调用错误,例如 host-not-found , connection-failure 等。
在声明接口(客户端使用的接口)之后,我们实现服务器端对象,并提供如图所示的 greet()方法。它使用一个简单的格式字符串来格式化问候语。
public class GreetingObject implements Greeting{ private String fmtString = "Hello, %s"; public String greet(String name) { return String.format(this.fmtString, name); }}
服务器主方法
现在让我们将所有这些内容整理在一起并实现 main()服务器的方法。让我们完成每个相关步骤。
Greeting greeting = new GreetingObject();
Greeting stub = (Greeting)UnicastRemoteObject.exportObject(greeting, 0);
String name = "Greeting";Registry registry = LocateRegistry.getRegistry(port);registry.rebind(name, stub);
完整的主要方法。
import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;import java.rmi.server.UnicastRemoteObject;public class Main{ static public void main(String[] args) throws Exception { if ( args.length == 0 ) { System.err.println("usage: java Main port#"); System.exit(1); } int index = 0; int port = Integer.parseInt(args[index++]); String name = "Greeting"; Greeting greeting = new GreetingObject(); Greeting stub = (Greeting)UnicastRemoteObject.exportObject(greeting, 0); Registry registry = LocateRegistry.getRegistry(port); registry.rebind(name, stub); System.out.println("Greeting bound to \"" + name + "\""); }}
构建服务器
让我们现在研究构建服务器。为简单起见,我们在Linux上使用命令行而不是使用Maven等构建工具进行构建。
以下代码将源文件编译为目标目录中的类文件。
rm -rf targetmkdir targetjavac -d target src/server/*.java
将类文件收集到JAR文件中以供执行。
jar cvf target/rmi-server.jar -C target server
我们还将收集将客户端编译到JAR库中所需的接口文件。
jar cvf target/rmi-lib.jar -C target server/Greeting.class
实现客户端
现在让我们研究实现用于调用服务器对象方法的客户端。
Registry registry = LocateRegistry.getRegistry(host, port);
Greeting greeting = (Greeting) registry.lookup(name);
System.out.println(name + " reported: " + greeting.greet(myName));
完整的客户端代码:
package client;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;import server.Greeting;public class Client{ static public void main(String[] args) throws Exception { if ( args.length != 3 ) { System.err.println("usage: java Client host port myName"); System.exit(1); } int index = 0; String host = args[index++]; int port = Integer.parseInt(args[index++]); String myName = args[index++]; String name = "Greeting"; Registry registry = LocateRegistry.getRegistry(host, port); Greeting greeting = (Greeting) registry.lookup(name); System.out.println(name + " reported: " + greeting.greet(myName)); }}
RMI注册表
现在让我们运行服务器程序,以便它可以开始处理请求。
java -cp target/rmi-server.jar server.Main 1099# throwsException in thread "main" java.rmi.ConnectException: Connection refused to host: xxx; nested exception is: java.net.ConnectException: Connection refused
此异常是什么? 连接被拒绝。
出现此异常的原因是:从服务器代码中注意到它尝试连接到端口1099上的本地注册表。如果失败,则结束
解决方案是运行RMI注册表。 RMI注册表是Java虚拟机附带的程序,称为 rmiregistry 。它应位于Java虚拟机安装的 bin 目录中。运行它非常简单:
/usr/lib/jvm/jdk1.8.0_71/bin/rmiregistry
默认情况下,注册表会监听端口1099。要使其监听另一个端口,请指定端口号,如下所示:
/usr/lib/jvm/jdk1.8.0_71/bin/rmiregistry 1100
检查使用netstat命令在指定端口上确实存在一个侦听器:
netstat -an -t tcp -p | grep LISTEN...tcp6 0 0 :::1100 :::* LISTEN 23450/rmiregistry
运行服务器
现在让我们尝试再次运行服务器。
java -cp target/rmi-server.jar server.Main 1100# throwsjava.rmi.UnmarshalException: error unmarshalling arguments...Caused by: java.lang.ClassNotFoundException: server.Greeting...
又是一个例外!这次是什么?
服务器无法加载接口类 server.Greeting 。发生这种情况是因为RMI注册表无法加载所需的类。因此,您需要指定所需类的位置。一种实现方法是指定CLASSPATH环境变量:
CLASSPATH=../../junk/target/rmi-lib.jar /usr/lib/jvm/jdk1.8.0_71/bin/rmiregistry 1100
尝试再次运行服务器将给出:
java -cp target/rmi-server.jar server.Main 1100# printsGreeting bound to "Greeting"
现在服务器正在运行。
运行客户端
在所有部分组装并执行之后,运行客户端非常简单。它需要适当的JAR才能执行。这些包括包含 main()方法的类和接口类。它接受指示运行RMI注册表的位置的参数以及问候语的名称。
java -cp target/rmi-client.jar:target/rmi-lib.jar client.Client localhost 1100 Peter# printsGreeting reported: Hello, Peter
摘要
Java RMI提供了API和工具来简化远程代码执行。您可以实现一个向Java RMI Registry注册服务对象的服务器。客户端可以查询注册表并获取用于调用服务方法的服务对象存根。如本例所示,这非常简单。
标签: