Java 中 RMI 的使用

RMI 介绍
RMI (Remote Method Invocation) 模型是一种分布式对象应用,使用 RMI 技术可以使一个 JVM 中的对象,调用另一个 JVM 中的对象方法并获取调用结果。这里的另一个 JVM 可以在同一台计算机也可以是远程计算机。因此,RMI 意味着需要一个 Server 端和一个 Client 端。
Server 端通常会创建一个对象,并使之可以被远程访问。
- 这个对象被称为远程对象。
- Server 端需要注册这个对象可以被 Client 远程访问。
Client 端调用可以被远程访问的对象上的方法,Client 端就可以和 Server 端进行通信并相互传递信息。
说到这里,是不是发现使用 RMI 在构建一个分布式应用时十分方便,它和 RPC 一样可以实现分布式应用之间的互相通信,甚至和现在的微服务思想都十分类似。
RMI 工作原理
正所谓 “知其然知其所以然”,在开始编写 RMI 代码之前,有必要了解一下 RMI 的工作原理,RMI 中 Client 端是和 Server 端是如何通信的呢?
下图的可以帮助我们理解RMI 的工作流程。

从图中可以看到,Client 端有一个被称 Stub 的东西,有时也会被成为存根,它是 RMI Client 的代理对象,Stub 的主要功能是请求远程方法时构造一个信息块,RMI 协议会把这个信息块发送给 Server 端。
这个信息块由几个部分组成:
- 远程对象标识符。
- 调用的方法描述。
- 编组后的参数值(RMI协议中使用的是对象序列化)。
既然 Client 端有一个 Stub 可以构造信息块发送给 Server 端,那么 Server 端必定会有一个接收这个信息快的对象,称为 Skeleton 。
它主要的工作是:
- 解析信息快中的调用对象标识符和方法描述,在 Server 端调用具体的对象方法。
- 取得调用的返回值或者异常值。
- 把返回值进行编组,返回给客户端 Stub.
到这里,一次从 Client 端对 Server 端的调用结果就可以获取到了。
RMI 开发
通过上面的介绍,知道了 RMI 的概念以及 RMI 的工作原理,下面介绍 RMI 的开发流程。
这里会通过一个场景进行演示,假设 Client 端需要查询用户信息,而用户信息存在于 Server 端,所以在 Server 端开放了 RMI 协议接口供客户端调用查询。
RMI Server
Server 端主要是构建一个可以被传输的类 User,一个可以被远程访问的类 UserService,同时这个对象要注册到 RMI 开放给客户端使用。
定义服务器接口(需要继承 Remote 类,方法需要抛出 RemoteException)。
package com.wdbyte.rmi.server; import java.rmi.Remote;
import java.rmi.RemoteException; /**
* RMI Server
*
* @author www.wdbyte.com
* @date 2021/05/08
*/
public interface UserService extends Remote { /**
* 查找用户
*
* @param userId
* @return
* @throws RemoteException
*/
User findUser(String userId) throws RemoteException;
}
User 对象在步骤 3 中定义。
实现服务器接口(需要继承 UnicastRemoteObject 类,实现定义的接口)。
package com.wdbyte.rmi.server; import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject; /**
* @author www.wdbyte.com
* @date 2021/05/08
*/
public class UserServiceImpl extends UnicastRemoteObject implements UserService { protected UserServiceImpl() throws RemoteException {
} @Override
public User findUser(String userId) throws RemoteException {
// 加载在查询
if ("00001".equals(userId)) {
User user = new User();
user.setName("金庸");
user.setAge(100);
user.setSkill("写作");
return user;
}
throw new RemoteException("查无此人");
}
}
定义传输的对象,传输的对象需要实现序列化(Serializable)接口。
需要传输的类一定要实现序列化接口,不然传输时会报错。IDEA 中如何生成 serialVersionUID,在文章末尾也附上了简单教程。
package com.wdbyte.rmi.server; import java.io.Serializable; /**
*
* @author www.wdbyte.com
* @date 2021/05/08
*/
public class User implements Serializable { private static final long serialVersionUID = 6490921832856589236L; private String name;
private Integer age;
private String skill; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} public String getSkill() {
return skill;
} public void setSkill(String skill) {
this.skill = skill;
} @Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", skill='" + skill + '\'' +
'}';
}
}
注册( rmiregistry)远程对象,并启动服务端程序。
服务端绑定了 UserService 对象作为远程访问的对象,启动时端口设置为 1900。
package com.wdbyte.rmi.server; import java.rmi.Naming;
import java.rmi.registry.LocateRegistry; /**
* RMI Server 端
*
* @author https://www.wdbyte.com
* @date 2021/05/08
*/
public class RmiServer { public static void main(String[] args) {
try {
UserService userService = new UserServiceImpl();
LocateRegistry.createRegistry(1900);
Naming.rebind("rmi://localhost:1900/user", userService);
System.out.println("start server,port is 1900");
} catch (Exception e) {
e.printStackTrace();
}
}
}
RMI Client
相比 Server 端,Client 端就简单的多。直接引入可远程访问和需要传输的类,通过端口和 Server 端绑定的地址,就可以发起一次调用。
package com.wdbyte.rmi.client;
import java.rmi.Naming;
import com.wdbyte.rmi.server.User;
import com.wdbyte.rmi.server.UserService;
/**
* @author https://www.wdbyte.com
* @date 2021/05/08
*/
public class RmiClient {
public static void main(String args[]) {
User answer;
String userId = "00001";
try {
// lookup method to find reference of remote object
UserService access = (UserService)Naming.lookup("rmi://localhost:1900/user");
answer = access.findUser(userId);
System.out.println("query:" + userId);
System.out.println("result:" + answer);
} catch (Exception ae) {
System.out.println(ae);
}
}
}
RMI 测试
启动 Server 端。
start server,port is 1900
启动 Client 端。
query:00001
result:User{name='金庸', age=100, skill='写作'}
如果 Client 端传入不存在的 userId。
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.RemoteException: 查无此人
serialVersionUID 的生成
IDEA 中生成 serialVersionUID,打开设置,如下图所示勾选。

选中要生成 serialVersionUID 的类,按智能提示快捷键。

参考
[1] https://docs.oracle.com/javase/tutorial/rmi/overview.html
订阅
文章会在博客和公众号同步更新。

Java 中 RMI 的使用的更多相关文章
- 关于<Java 中 RMI、JNDI、LDAP、JRMP、JMX、JMS那些事儿(上)>看后的一些总结-1
原文地址:https://www.anquanke.com/post/id/194384#h3-3 1.java rmi 关于rmi客户端和服务端通信的过程,java的方法都实现在rmi服务端,客户端 ...
- Java中RMI远程调用demo
Java远程方法调用,即Java RMI(Java Remote Method Invocation),一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序可以调用远程服务器上的对象.远 ...
- Java中RMI框架
嘎嘎,有空写……先记着了
- Java开发中RMI和webservice区别和应用领域
Java开发中RMI和webservice区别和应用领域 一.RMI和webservice区别和联系 0. 首先,都是远程调用技术. 1. RMI是在TCP协议上传递可序列化的java对象(使用Str ...
- java回顾rmi
搞java的不懂rmi好像说不过去.. ,复习一遍. 参照http://www.iteye.com/topic/173909 http://lzj0470.iteye.com/blog/426760 ...
- 【译】Java中的对象序列化
前言 好久没翻译simple java了,睡前来一篇. 译文链接: http://www.programcreek.com/2014/01/java-serialization/ 什么是对象序列化 在 ...
- java的RMI(Remote Method Invocation)
RMI 相关知识RMI全称是Remote Method Invocation-远程方法调用,Java RMI在JDK1.1中实现的,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网络 ...
- java中serializable
java中serializable是一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才是可序列化的.因此如果要序列化某些类的对象,这些类就必须实现Serializable接 ...
- Java中的SerialVersionUID
Java中的SerialVersionUID 序列化及SergalVersionUID困扰着许多Java开发人员.我经常会看到这样的问题,什么是SerialVersionUID,如果实现了Serial ...
随机推荐
- hessian简单介绍
Hessian是基于HTTP的轻量级远程服务解决方案,Hessian像Rmi一样,使用二进制消息进行客户端和服务器端交互.但与其他二进制远程调用技术(例如Rmi)不同的是,它的二进制消息可以移植其他非 ...
- webpack4.x 从零开始配置vue 项目(二)基础搭建loader 配置 css、scss
序 上一篇已经把基本架子搭起来了,现在来增加css.scss.自动生成html.css 提取等方面的打包.webpack 默认只能处理js模块,所以其他文件类型需要做下转换,而loader 恰恰是做这 ...
- PTA 数组元素的区间删除
6-6 数组元素的区间删除 (20 分) 给定一个顺序存储的线性表,请设计一个函数删除所有值大于min而且小于max的元素.删除后表中剩余元素保持顺序存储,并且相对位置不能改变. 函数接口定义: ...
- [换根DP]luogu P3647 [APIO2014]连珠线
题面 https://www.luogu.com.cn/problem/P3647 不重复地取树中相邻的两条边,每次得分为两条边权和,问最大得分 分析 容易想到状态 f[i][0/1] 分别表示 i ...
- Hystrix 实战经验分享
一.背景 Hystrix是Netlifx开源的一款容错框架,防雪崩利器,具备服务降级,服务熔断,依赖隔离,监控(Hystrix Dashboard)等功能. 尽管说Hystrix官方已不再维护,且有A ...
- Python爬虫系列之爬取美团美食板块商家数据(一)
主要思路 目的: 根据输入的城市名,爬取该城市美团美食板块所有商家的数据.数据包括: 店名.评分.评论数量.均价.地址, 并将这些数据存入Excel中. 最后尝试对爬取到的数据做一个简单的分析. 克服 ...
- 自己挖的坑自己填--jxl进行Excel下载堆内存溢出问题
今天在进行使用 jxl 进行 Excel 下载时,由于数据量大(4万多条接近5万条数据的下载),数据结构过于负责,存在大量大对象(虽然在对象每次用完都设置为null,但还是存在内存溢出问题),加上本地 ...
- 图像Resize方式对深度学习模型效果的影响
在基于卷积神经网络的应用过程中,图像Resize是必不可少的一个步骤.通常原始图像尺寸比较大,比如常见监控摄像机出来的是1080P高清或者720P准高清画面,而网络模型输入一般没有这么大,像Yolo系 ...
- 关于误删除elasticSearch 索引,怎么能快速找回?
背景 之前公司小王在工作中清理elasticSearch 索引,不小心使用脚本清空了最近使用的重要索引,导致开发无法准确的进行定位生产问题,造成了很大困扰. 当时我们的生产环境中是这样配置日志系统的: ...
- 梳理一下最近准备蓝桥杯时学习DP问题的想法
学习时间不长,记录的只是学习过程的思路和想法,不能保证正确,代码可以在acwing上AC. 01背包问题: 1.首先是简单的01背包问题 2.先确定状态,f[i][j]表示有第i件物品,时间为j的最大 ...