白袍的小行星

Java安全基础——RMI

字数统计: 859阅读时长: 3 min
2021/06/01 Share

RMI即Remote Method Invocation,远程方法调用,也就是一个JVM中的代码可以远程调用另一个JVM中的方法,这种方法的优点在于可以提高复用性,不必重复造轮子,缺点则是可靠性很差。

RMI分为三个部分:

  • Registry:提供注册服务以及获取服务,实际上类似中间人
  • Server:提供远程方法,向 Registry注册自身的服务
  • Client:使用远程方法,从 Registry获取远程方法的相关信息

ServerClient之间是通过JRMP协议通信的,这是逻辑上的表达,实际上两者并不直接通信,而是通过其中的Stub对象以及Skeleton对象。

在Java中,引用类型的参数是以引用方式传递的,那么在不同JVM中,对象的引用就成了一个问题,一般有两种解决办法:

  1. 将对象进行序列化后传递,也就是变为值传递
  2. 共享这个对象

下面写一个例子,来熟悉一下RMI,首先要定义一个继承了Remote的远程接口,其中每一个方法都需要抛出RemoteException异常:

public interface remoteInterface extends Remote{
String remoteTest(String name)throws RemoteException;
}

接着实现这个接口:

public class RMIRemoteTest  implements RMIInterface{

@Override
public String remoteTest(String name) throws RemoteException {
System.out.println(name);
return name;
}
}

创建Registry

public class RMIRegistry {
public static void main(String[] args) throws InterruptedException {
try{
LocateRegistry.createRegistry(9966);
} catch (RemoteException e) {
e.printStackTrace();
}
CountDownLatch countDownLatch = new CountDownLatch(1);
countDownLatch.await();
}
}

这里的Registry类似于Web服务中DNS的作用,客户端提供远程对象的标识符来获得其引用。

接下来可以写Server端了,首先实例化一个之前继承了接口的类,再进行注册和绑定:

public class RMIServer {
public static void main(String[] args) {
RMIInterface rmiRemoteTest = new RMIRemoteTest();
try{
RMIInterface stub = (RMIInterface) UnicastRemoteObject.exportObject(rmiRemoteTest, 9999);
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9966);
registry.bind("test", stub);
} catch (RemoteException | AlreadyBoundException e) {
e.printStackTrace();
}
}
}

首先实例化了一个远程对象,从编程角度来看,实现或继承了java.rmi.Remote接口的都可以被看作远程对象。
接着利用UnicastRemoteObject类的exportObject方法,导出了一个远程对象的引用。
下面则实例化了一个Registry对象,并使用名称,将该服务注册到Registry.

最后来创建一个Client端:

public class RMIClient {
public static void main(String[] args){
try{
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9966);
RMIInterface rmiRemoteTest = (RMIInterface) registry.lookup("test");
System.out.println(rmiRemoteTest.remoteTest("adan0s"));
} catch (RemoteException | NotBoundException e) {
e.printStackTrace();
}
}
}

首先获取到Registry对象的引用,再通过lookup方法查找获取相应服
务,最后调用远程方法。

在这个过程中还遇到几个坑:

  1. 如果RegistryServer在不同类,那就要先启动前者,再启动后者,最后启动Client
  2. 动态代理问题,后面专门写相关文章吧
  3. 演示时是在同一台机器上运行的,实际环境中要保证这个接口在客户端和服务端上都存在

RMI流程如下:

  1. 客户端连接Registry,查找名称为test的对象
  2. Registry返回经过序列化的数据,也就是这个对象
  3. 客户端进行反序列化,得到一个远程对象,进行连接
  4. 在新的连接中调用远程方法

参考文章

从懵逼到恍然大悟之Java中RMI的使用_lmy86263的博客-CSDN博客_rmi协议
java rmi 使用教程及原理 - SegmentFault 思否

CATALOG
  1. 1. 参考文章