返回 导航

其他

hangge.com

Java - 远程方法调用RMI使用详解(附样例)

作者:hangge | 2022-12-26 08:10

1,什么是 RMI?

(1)RMIRemote Method Invocation)是 Java 的一种远程方法调用技术,它允许你从一个 Java 程序调用另一个 Java 程序的方法,即使这两个程序位于不同的计算机上。
(2)RMI 是一种用于实现远程过程调用(RPCRemote procedure call)的 Java API, 能直接传输序列化后的 Java 对象和分布式垃圾收集。它的实现依赖于Java虚拟机(JVM),因此它仅支持从一个 JVM 到另一个 JVM 的调用。
(3)RMI 使得开发人员可以创建分布式应用程序,其中一个 Java 程序可以调用另一个 Java 程序上的方法,就好像调用本地对象的方法一样。

2,RMI 的工作流程

(1)首先,服务器创建一个远程对象并将其注册到注册表中。
(2)客户端可以获取注册表中存储的对象的引用
(3)当客户端调用远程对象的方法时,实际上会在与客户端 JVM 在同一 JVM 中的存根对象(Stub)上调用该方法。
(4)存根对象(Stub)会创建一条消息,其中包含方法的名称以及其参数(称为封装),并将此消息发送到位于服务器 JVM 中的相关骨架对象(Skeleton)。
(5)骨架对象(Skeleton)会从消息中提取方法名和参数(称为解封装),并调用与其关联的远程对象上的适当方法。
(6)远程对象执行该方法并将返回值传回骨架对象(Skeleton)。
(7)骨架对象(Skeleton)再将返回值封装在消息中并将此消息发送到存根对象(Stub)。
(8)存根对象(Stub)从消息中解封装返回值,并将该值返回给客户端程序

3,基本用法

(1)首先,我们需要定义一个远程接口,该接口中包含要在远程调用的方法:
注意:可以远程的接口必须扩展 RMI 定义的 Remote 接口。并且该接口中的每个方法都必须声明抛出 java.rmi.RemoteException 异常,因为这些方法是远程调用的,存在许多可能导致失败的因素,例如网络故障等。
public interface HelloService extends Remote {
  String sayHello(String name) throws RemoteException;
}

(2)接下来,我们需要实现该接口,并提供实际的方法实现:
注意:远程对象必须继承 UniCastRemoteObject 类,保证客户端访问获得远程对象时,该远程对象将会把自身的一个拷贝以 Socket 的形式传输给客户端。
public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {

  protected HelloServiceImpl() throws RemoteException {
    super();
  }

  @Override
  public String sayHello(String name) throws RemoteException {
    return "你好, " + name + "!";
  }
}

(3)接下来,我们需要创建一个 RMI 服务器来提供远程访问:
public class Server {

  public static void main(String[] args) {
    try {
      // 创建远程对象
      HelloService service = new HelloServiceImpl();
      // 创建并导出接受指定 port 的本地主机上的 RMI 连接,并指定 server 的 RMI 名称
      LocateRegistry.createRegistry(1099);
      // 将远程对象注册到 RMI 注册服务器上,并命名为 Hello
      Naming.bind("rmi://127.0.0.1:1099/Hello", service);
      System.out.println("服务器启动成功!");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

(4)接下来,我们可以创建一个客户端来远程调用服务器上的方法:
public class Client {
  public static void main(String[] args) {
    try {
      // 在 RMI 注册服务器上查找名称为 Hello 的远程对象
      HelloService service = (HelloService) Naming.lookup("rmi://127.0.0.1:1099/Hello");
      // 调用远程方法
      String result = service.sayHello("hangge");
      // 输出调用结果
      System.out.println(result);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

4,序列化传输对象

(1)如果需要传输自定义对象时,传输的类一定要实现序列化接口(Serializable),不然传输时会报错。
public class User implements Serializable {
  private String userName;
  private Integer age;

  public User(String userName, Integer age) {
    this.userName = userName;
    this.age = age;
  }

  public String getUserName() {
    return userName;
  }

  public void setUserName(String userName) {
    this.userName = userName;
  }

  public Integer getAge() {
    return age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }
}

(2)我们修改下远程接口以及接口的实现:
// 远程方法接口
public interface HelloService extends Remote {
  User sayHello(String name) throws RemoteException;
}

// 远程方法接口的实现
public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {
  protected HelloServiceImpl() throws RemoteException {
    super();
  }

  @Override
  public User sayHello(String name) throws RemoteException {
    User user = new User(name + "先生", 100);
    return user;
  }
}

(3)客户端代码修改如下:
public class Client {
  public static void main(String[] args) {
    try {
      // 在 RMI 注册服务器上查找名称为 Hello 的远程对象
      HelloService service = (HelloService) Naming.lookup("rmi://127.0.0.1:1099/Hello");
      // 调用远程方法
      User result = service.sayHello("hangge");
      // 输出调用结果
      System.out.println(result.getUserName());
      System.out.println(result.getAge());
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

5,超时设置

(1)在使用 RMI 时,可能会遇到连接超时的情况。我们可以设置超时时间,这样当客户端在尝试连接服务器时等待的时间超过了预定义的超时时间时便会抛出异常。
提示:在 RMI 中,可以通过设置系统属性 sun.rmi.transport.tcp.responseTimeout 来设置连接超时时间。该属性控制 RMI 在接收到服务器的响应之前等待的时间(以毫秒为单位)。

(2)我们可以在启动 JVM 时使用 -D 选项来设置该属性(这将设置超时时间为 10 秒):
java -Dsun.rmi.transport.tcp.responseTimeout=10000 MyClient

(3)也可以通过调用 System.setProperty 方法来设置该属性:
System.setProperty("sun.rmi.transport.tcp.responseTimeout", "10000");
评论

全部评论(0)

回到顶部