关于Java RMI注册表及其使用方法的全部信息

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()服务器的方法。让我们完成每个相关步骤。

  • The first step is to create the server object implementation.
    Greeting greeting = new GreetingObject();
  • Next, we obtain a stub for the server object from the RMI runtime. The stub implements the same interface as the server object. However the method implements the required communication with the remote server object. This stub is used by the client to transparently invoke the method on the server object.
    Greeting stub = (Greeting)UnicastRemoteObject.exportObject(greeting, 0);
  • Once the stub is obtained, we hand this stub over to the RMI registry to bind to a specified named service. When the client requests an implementation of this service, it receives the stub which knows how to communicate with the server object.In the following, the static method LocateRegistry.getRegistry() is used to obtain the local registry reference. The rebind() method is then used to bind the name to the stub.
    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

    实现客户端

    现在让我们研究实现用于调用服务器对象方法的客户端。

  • As with the server, obtain a reference to the registry, specifying the hostname where the registry is running, and the port number.
    Registry registry = LocateRegistry.getRegistry(host, port);
  • Next, lookup the service in the registry. The lookup() method returns a stub which can be used for invoking services.
    Greeting greeting = (Greeting) registry.lookup(name);
  • And invoke the method passing the required arguments. Here, we get the greeting by passing the name and printing it out.
    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注册服务对象的服务器。客户端可以查询注册表并获取用于调用服务方法的服务对象存根。如本例所示,这非常简单。

  • 标签: