远程方法调用,现在更多的使用RPC来处理,至于RMI好像没有那么多了,最近闹的火热的log4j2漏洞,又让几个关键词jndi,rmi,ldap频繁出现;对于我这种面向Spring编程的javer而言,这些是啥? 干嘛用的?为啥漏洞这么多?
接下来简单学习下RMI的基本知识点
1. RMI科普
参考:https://www.jianshu.com/p/de85fad05dcb
Java RMI,即 远程方法调用(Remote Method Invocation),一种用于实现远程过程调用(RPC)(Remote procedure call)的Java API,能直接传输序列化后的Java对象和分布式垃圾收集。它的实现依赖于Java虚拟机(JVM),因此它仅支持从一个JVM到另一个JVM的调用。

可以简单的将RMI理解为jdk原生提供的rpc支持方式
2. 基础体验
基于上面的RMI架构图,要体验一下RMI的基本功能,非常简单了
2.1 服务端
要提供一个rmi服务端就比较简单了,不需要额外引入依赖,直接使用
类似于我们常见的rpc框架,先提供一个接口,终点注意它需要继承Remote接口
1 2 3 4 5
| import java.rmi.Remote; public interface HelloService extends Remote { String hello() throws RemoteException; }
|
对应的实现类,重点注意继承自UnicastRemoteObject
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.time.LocalDateTime;
public class HelloServiceImpl extends UnicastRemoteObject implements HelloService { protected HelloServiceImpl() throws RemoteException { }
@Override public String hello() throws RemoteException { return "hello: " + LocalDateTime.now(); } }
|
最后就是启动服务,提供一个上面的接口
1 2 3 4 5 6 7 8 9 10 11
| public class RmiServer {
public static void main(String[] args) throws Exception { Registry registry = LocateRegistry.createRegistry(8181); HelloService hello = new HelloServiceImpl(); registry.bind("hello", hello); System.out.println("服务已启动"); Thread.currentThread().join(); } }
|
2.2 客户端
客户端访问rmi服务就很简单了,两行代码即可
1 2 3 4 5 6 7 8 9
| public class RmiClient {
public static void main(String[] args) throws Exception{ Registry registry = LocateRegistry.getRegistry(8181); HelloService hello = (HelloService) registry.lookup("hello"); String response = hello.hello(); System.out.println(response); } }
|
2.3 测试
先启动服务端,再启动客户端,可以看到客户端会拿到一个HelloService的实例,可以直接像调用本地方法一下访问这个方法

注意上面这个case,客户端拿到实例,访问实例方法,这个逻辑是在哪里执行的呢?(客户端还是服务端?)
- 服务端执行(可以通过在实现类中添加一行日志,看下这个日志是在服务端输出的还是客户端输出的)
3.naming方式
除了上面的这种方式之外,使用Naming方式的也非常普遍,如下
服务端,新的写法如下
1 2 3 4 5 6
| public static void main(String[] args) throws Exception { Registry registry = LocateRegistry.createRegistry(8181); Naming.bind("rmi://localhost:8181/hello", hello); System.out.println("服务已启动"); Thread.currentThread().join(); }
|
客户端的写法如下
1 2 3 4 5 6
| public static void main(String[] args) throws Exception { String remoteAddr="rmi://localhost:8181/hello"; HelloService hello = (HelloService) Naming.lookup(remoteAddr); String response = hello.hello(); System.out.println(response); }
|
这种方式与前面的效果相同,区别在于当有多个服务端时,使用naming的方式,可以指定ip + 端口号来获取对应的服务提供者
一灰灰的联系方式
尽信书则不如无书,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
