详解Java 中 RMI 的使用
作者:未读代码 发布时间:2023-10-12 08:31:27
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 开放给客户端使用。
1.定义服务器接口(需要继承 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 中定义。
2.实现服务器接口(需要继承 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("查无此人");
}
}
3.定义传输的对象,传输的对象需要实现序列化(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 + '\'' +
'}';
}
}
4.注册( 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
来源:https://www.cnblogs.com/niumoo/p/14753782.html
猜你喜欢
- yml文件参数的读取附上一个较为常见的application.yml文件示例server: port: 9999 u
- 1. * 在 Spring Boot 可以可以在以下情况执行操作:在将请求发送到控制器之前在将响应发送给客户端之前2. * 使用下面实现
- 前言一般生成的PDF文档默认的文档底色为白色,我们可以通过一定方法来更改文档的背景色,以达到文档美化以及保护双眼的作用。 以下内容提供了Ja
- 在C# Winform 应用程序中,获取某网页的源文件,可以用以下方法:首先引入名称空间using System.IO;using Syst
- 无意中在一个国外的站点下到了一个利用WCF实现聊天的程序,作者是:Nikola Paljetak。研究了一下,自己做了测试和部分修改,感觉还
- 前言第二步理论上我们该写客户端了,但是,在此之前,需要先介绍下一些必要的方法以及操作。写代码还是要尽量的保证通用性,以便以后需要的时候可以拿
- 本文讲述的是Android中RelativeLayout、FrameLayout的用法。具体如下:RelativeLayout是一个按照相对
- 本文实例为大家分享了Android简单使用PopupWindow的的具体代码,供大家参考,具体内容如下思路1.在res下面创建一个menu文
- 一、效果图 二、实现思路1.界面上可以点开的各种实际都是按钮,创建9行9列的二维数组,然后
- 最近正式入坑Flutter,首先从环境搭建开始,看了网上好多关于Windows环境搭建的资料,基本都是按官方文档写的,看完的感受是,还不如直
- 介绍技术之前有用eureka 现在用nacos工作流和gateway接口数据流向数据表新建项目新建cloud-删除src-新建modleEu
- 概述这里是Mac环境,如果是window环境的同学,在环境搭建和工具上可以选择Window环境的。先看看需要到的工具;1、apktool:h
- 编写一个简单的mybatis进行插入数据的实例1 数据库建表 其中建表dob=Date of Birth 的意思create table s
- 一、前言 本学期学习了JAVA语言,在学期的结束,写一个有操作界面,与数据库关联的管理系统,用来巩固自己本学习所学的知识。 用到的知识:JA
- 一、使用@CrossOrigin注解在controller类上加上@CrossOrigin注解,就能对这个类下面所有方法进行跨域访问了@Cr
- 本文实例为大家分享了java实现Dijkstra算法的具体代码,供大家参考,具体内容如下1 问题描述何为Dijkstra算法?Dijkstr
- Java读取json数据并存入数据库1. pom依赖<dependency> &nbs
- 这篇文章主要介绍了java获取当前时间的四种方法代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 一、概述;从字面上讲,就是停止这个世界,看到这个字眼,就觉得这是可怕的事情,那到底什么是stop-the-world?stop-the-wo
- 前言:我们在Android手机上面有时候会遇到监听手机软键盘按键的时候,例如:我们在浏览器输入url完毕后可以点击软键盘右下角的“Go”按键