RMI全称是Remote Method Invocation,即远程⽅法调⽤,主要功能就是可以让某个Java虚拟机上的对象调⽤另⼀个Java虚拟机中对象上的方法。
例子🌰
服务端如下:
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
public class RMIServer {
public interface IRemoteHelloRMI extends Remote {
public String hello() throws RemoteException;
}
public class RemoteHelloRMI extends UnicastRemoteObject implements
IRemoteHelloRMI {
protected RemoteHelloRMI() throws RemoteException {
super();
}
public String hello() throws RemoteException {
System.out.println("something call me");
return "Hello RMI";
}
}
private void start() throws Exception {
RemoteHelloRMI h = new RemoteHelloRMI();
LocateRegistry.createRegistry(15080);
Naming.rebind("rmi://127.0.0.1:15080/Hello", h);
}
public static void main(String[] args) throws Exception {
new RMIServer().start();
}
}
上面内容主要分为:
- ⼀个继承了 java.rmi.Remote 的接⼝,其中定义我们要远程调⽤的函数,⽐如这⾥的 hello(),
- ⼀个实现了IRemoteHelloRMI接⼝的类
- ⼀个函数start(),⽤来创建Registry,并将上⾯的类实例化后绑定到⼀个地址。
客户端实现如下:
import java.rmi.Naming;
public class RMIClient {
public static void main(String[] args) throws Exception {
RMIServer.IRemoteHelloRMI hello = (RMIServer.IRemoteHelloRMI)
Naming.lookup("rmi://127.0.0.1:15080/Hello");
String ret = hello.hello();
System.out.println( ret);
}
}
先后运行服务端和客户端,结果如下:
通过抓包
可以知道这个流程其实建立了两个TCP链接:
- 与RMI Registry的通信
- 与RMI Server的通信
另外可以推荐一个不错的解析反序列化内容的工具SerializationDumper
它是一种以更易于阅读的形式转储和重新构建Java序列化流和Java RMI包内容的工具。
上面内容解析见:https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html
调用流程图如下:
RMI Registry就像⼀个⽹关,他⾃⼰是不会执⾏远程⽅法的,但RMI Server可以在上⾯注册⼀个Name到对象的绑定关系;RMI Client通过Name向RMI Registry查询,得到这个绑定关系,然后再连接RMI
Server;最后,远程⽅法实际上在RMI Server上调⽤。