Java 中 RMI 的使用

简介: Java 中 RMI 的使用

微信图片_20220414170525.jpg

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 的工作流程。

微信图片_20220414170529.jpgRMI Connection

从图中可以看到,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;
}
  1. 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("查无此人");
    }
}
  1. 定义传输的对象,传输的对象需要实现序列化(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 + '\'' +
            '}';
    }
}
  1. 注册( 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,打开设置,如下图所示勾选。

微信图片_20220414170532.jpgIDEA 设置

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

微信图片_20220414170535.jpgIDEA serialVersionUID

参考

[1] https://docs.oracle.com/javase/tutorial/rmi/overview.html

相关文章
|
存储 Java
【Java基础】- RMI原理和使用详解
【Java基础】- RMI原理和使用详解
284 0
|
10月前
|
Java 程序员
百度搜索:蓝易云【Java网络编程RMI框架详解。】
RMI框架提供了一种方便的方式来实现分布式系统中的远程通信和方法调用。它简化了网络编程的复杂性,使得开发人员可以专注于业务逻辑而不必过多关注底层网络细节。通过RMI框架,Java开发人员可以轻松构建可扩展、可靠的分布式应用程序。
39 2
|
安全 Java
Java RMI 反序列化漏洞-远程命令执行
Java RMI 反序列化漏洞-远程命令执行
405 0
|
存储 安全 网络协议
JAVA EE十三大规范(1)RMI(全网最深入浅出)
1.概述 RPC: RPC(Remote Procedure Call),一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议,RPC可以用HTTP协议实现,并且用HTTP是建立在 TCP 之上最广泛使用的 RPC,但是互联网公司往往用自己的私有协议,比如鹅厂的JCE协议,私有协议不具备通用性但是相比于HTTP协议,RPC采用二进制字节码传输,更加高效也更加安全。 用一个比较形象的例子来形容,你老婆出去打麻将,然后想起家里洗衣机里的衣服还没洗,于是打电话给在家里的你,叫你把洗衣机里的衣服洗了,这就是远程过程调用。微服务中服务的调用底层就是使用的RPC机制。
146 0
|
存储 Java 数据库连接
RMI AND CORBA简介——java菜鸟成长记
RMI AND CORBA简介——java菜鸟成长记
178 0
RMI AND CORBA简介——java菜鸟成长记
|
设计模式 消息中间件 监控
初探RMI设计模式实现大家——JMX(Java Management Extension)
初探RMI设计模式实现大家——JMX(Java Management Extension)
219 0
初探RMI设计模式实现大家——JMX(Java Management Extension)
|
Dubbo Java 应用服务中间件
java远程调用之RMI(终于可以自己写代码控制别人电脑了)
之前在研究生课程当中学了分布式系统这门课,而且还是自己的导师讲的这门课,在课堂上迷迷糊糊的晃悠了一学期,除了听见几个名词,也没太多印象。正好这几天用到远程过程调用,使用的是gRPC,想到之前上课听过,于是把这块的知识从书到教程好好地补充了一下。 本篇文章尽量不啰嗦,它的重要性自己可以私下了解一下。
343 0
java远程调用之RMI(终于可以自己写代码控制别人电脑了)
|
Dubbo 前端开发 网络协议
一文告诉你 Java RMI 和 RPC 的区别!
RPC(Remote Procedure Call Protocol)远程过程调用协议,通过网络从远程计算机上请求调用某种服务。
一文告诉你 Java RMI 和 RPC 的区别!
|
Java Spring 调度
Java Spring RMI一些尝试
最近在做一个调度服务器的项目,因为各个服务器上面的定时任务比较多,分别执行的话运维检查起来比较麻烦.就想到搞一个专门的调度服务器来统一管理这些任务.因为随时可能增加新的服务器或者新的命令,要是全写在一起每次要把整个程序替换掉,上线不方便.
1105 0