概念

​ **RMI: 远程方法调用(Remote Method Invocation)**,它支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用。

Java RMI: 用于不同虚拟机之间的通信,这些虚拟机可以在不同的主机上、也可以在同一个主机上;一个虚拟机中的对象调用另一个虚拟上中的对象的方法,只不过是允许被远程调用的对象要通过一些标志加以标识。

Java使用一种序列化的方式来实现远程调用。

原理

首先了解一下RMI的交互图,RMI主要由三部分组成,即RMI Client、RMI Service、RMI Registry

  • 首先,先启动rmiregistry服务,启动时可以指定服务监听的端口,也可以使用默认的端口(1099)。
  • 其次,server端在本地先实例化一个提供服务的实现类,然后通过RMI提供的aming/Context/Registry(等类的bindrebind方法将刚才实例化好的实现类注册到rmiregistry上并对外暴露一个名称。
  • 最后,client端通过本地的接口和一个已知的名称(即rmiregistry暴露出的名称)再使用RMI提供的Naming/Context/Registry等类的lookup方法从RMIService那拿到实现类。这样虽然本地没有这个类的实现类,但所有的方法都在接口里了,便可以实现远程调用对象的方法了。

Java实现

Server:一个RMI Server分为三部分:

  1. 一个继承了了 java.rmi.Remote 的接⼝口,其中定义我们要远程调⽤用的函数
  2. 一个实现了此接口的类
  3. 一个主类,用来创建Registry,并将上面的类实例化后绑定到一个地址。这就是我们所谓的Server
    了。

比如下面这个样例

IHello.java

1
2
3
4
5
6
7
8
9
package Server;

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

public interface IHello extends Remote {
public String sayHelloToSomeBody(String someBodyName) throws RemoteException;
}

HelloImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package Server;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class HelloImpl extends UnicastRemoteObject implements IHello {
public HelloImpl() throws RemoteException {
super();
}
public String sayHelloToSomeBody(String someBodyName) throws RemoteException {
System.out.println("Connected sucessfully!");
return "你好," + someBodyName + "!";
}
}

HelloServer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package Server;

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class HelloServer {
public static void main(String args[]) {
try {
IHello rhello = new HelloImpl();
LocateRegistry.createRegistry(8888);
// 如果配置在远程服务器,把地址换成你的ip
System.setProperty("java.rmi.server.hostname","127.0.0.1");
Naming.bind("rmi://localhost:8888/RHello", rhello);
System.out.println(">>>>>INFO:远程IHello对象绑定成功!");
} catch (RemoteException e) {
System.out.println("创建远程对象发生异常!");
e.printStackTrace();
} catch (AlreadyBoundException e) {
System.out.println("发生重复绑定对象异常!");
e.printStackTrace();
} catch (MalformedURLException e) {
System.out.println("发生URL畸形异常!");
e.printStackTrace();
}
}
}

RMIClient.java

客户端直接调用服务端在远程开放的接口就可以实现远程调用的功能,这里使用Naming.lookup的方法来获取到实现类,在实现类的传输过程中,类对象以序列化的形式传输。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package Client;

import Server.IHello;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class RMIClient {
public static void main(String args[]) {
try {
IHello rhello = (IHello) Naming.lookup("rmi://127.0.0.1:8888/RHello");
System.out.println(rhello.sayHelloToSomeBody("XZLang"));
} catch (NotBoundException e) {
} catch (MalformedURLException e) {
e.printStackTrace();
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}

启动服务端:

执行客户端代码: