Java RMI 解析

1.什么是RMI?

Java RMI,即 远程方法调用(Remote Method Invocation),一种用于实现远程过程调用(RPC)(Remote procedure call)的Java API, 

能直接传输序列化后的Java对象和分布式垃圾收集。它的实现依赖于Java虚拟机(JVM),因此它仅支持从一个JVM到另一个JVM的调用。

2.工作原理:

方法调用从客户对象经 占位程序(Stub)远程引用层(Remote Reference Layer)传输层(Transport Layer)向下,传递给主机,然后再次经 传输层,向上穿过 远程调用层骨干网(Skeleton),到达 服务器对象。 
占位程序:扮演着远程服务器对象的代理的角色,使该对象可被客户激活。 
远程引用层:处理语义、管理单一或多重对象的通信,决定调用是应发往一个服务器还是多个。
传输层:实际的连接,并且追踪可以接受方法调用的远程对象。
骨干网:完成对 服务器对象实际的方法调用,并获取返回值。返回值向下经 远程引用层服务器端传输层传递回 客户端,再向上经 传输层和远程调用层返回。最后, 占位程序获得返回值。

3.RMI 远程调用步骤 

调用步骤: 

1.服务端创建远程对象

2.注册远程对象(bind( ) 或 rebind( ))

3.访问服务器并查找注册的远程对象(lookup() 方法)

4.返回服务器远程对象的存根

5.调用远程对象的方法

6.客户端本地存根和服务器骨架通信

7.骨架代理调用方法

8.返回方法的执行结果

9.骨架返回结果给存根

10.存根把结果返回给客户端

4.代码样例:

1) 定义一个远程接口

package com.icfcc.fcit.dp.modules.msg.express.core.rmi;

import java.rmi.Remote;
/**
* @Description
* 在Java中,只要一个类extends了java.rmi.Remote接口,即可成为存在于服务器端的远程对象,
* 供客户端访问并提供一定的服务。
* JavaDoc描述:Remote接口用于标识其方法可以从非本地虚拟机上
* 调用的接口。任何远程对象都必须直接或间接实现此接口。只有在“远程接口”
* (扩展 java.rmi.Remote 的接口)中指定的这些方法才可被远程调用。
*
* @date 2022/11/7 15:55
* @Version 1.0
* @Author gezongyang
*/
public interface IHello extends Remote {
    /*
     * extends了Remote接口的类或者其他接口中的方法若是声明抛出了RemoteException异常,
     * 则表明该方法可被客户端远程访问调用。
     */
    String sayHello(String name) throws java.rmi.RemoteException;
}

 2)远程接口实现类

package com.icfcc.fcit.dp.modules.msg.express.core.rmi;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
* @Description 远程对象必须实现java.rmi.server.UniCastRemoteObject类,这样才能保证客户端访问获得远程对象时,
* 该远程对象将会把自身的一个拷贝以Socket的形式传输给客户端,此时客户端所获得的这个拷贝称为“存根”,
* 而服务器端本身已存在的远程对象则称之为“骨架”。其实此时的存根是客户端的一个代理,用于与服务器端的通信,
* 而骨架也可认为是服务器端的一个代理,用于接收客户端的请求之后调用远程方法来响应客户端的请求
* @date 2022/11/7 15:59
* @Version 1.0
* @Author gezongyang
*/
public class HelloImpl extends UnicastRemoteObject implements IHello{
    // 这个实现必须有一个显式的构造函数,并且要抛出一个RemoteException异常
    protected HelloImpl() throws RemoteException {
        super();
    }

    private static final long serialVersionUID = 4077329331699640331L;
    public String sayHello(String name) throws RemoteException {
        return "Hello " + name + " ^_^ ";
    }
}

 3) 服务端

package com.icfcc.fcit.dp.modules.msg.express.core.rmi;

import java.rmi.registry.LocateRegistry;

/**
* @Description 注册远程对象,向客户端提供远程对象服务
* 远程对象是在远程服务上创建的,你无法确切地知道远程服务器上的对象的名称
* 但是,将远程对象注册到RMI Service之后,客户端就可以通过RMI Service请求
* 到该远程服务对象的stub了,利用stub代理就可以访问远程服务对象了
*
* @date 2022/11/7 16:01
* @Version 1.0
* @Author gezongyang
*/
public class HelloServer {
    public static void main(String[] args) {
        try {
            IHello hello = new HelloImpl();
            /* 生成stub和skeleton,并返回stub代理引用 */
            /* 本地创建并启动RMI Service,被创建的Registry服务将在指定的端口上侦听到来的请求
             * 实际上,RMI Service本身也是一个RMI应用,我们也可以从远端获取Registry:
             *     public interface Registry extends Remote;
             *     public static Registry getRegistry(String host, int port) throws RemoteException;
             */
            LocateRegistry.createRegistry(1099);
            /* 将stub代理绑定到Registry服务的URL上 */
            java.rmi.Naming.rebind("rmi://localhost:1099/hello", hello);
            System.out.print("Ready");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4)客户端

package com.icfcc.fcit.dp.modules.msg.express.core.rmi;

import java.rmi.Naming;

/**
* @Description TODO
* @date 2022/11/7 16:03
* @Version 1.0
* @Author gezongyang
*/
public class Hello_RMI_Client {
    public static void main(String[] args) {
        try {
            /* 从RMI Registry中请求stub
             * 如果RMI Service就在本地机器上,URL就是:rmi://localhost:1099/hello
             * 否则,URL就是:rmi://RMIService_IP:1099/hello
             */
            IHello hello = (IHello) Naming.lookup("rmi://localhost:1099/hello");
            /* 通过stub调用远程接口实现 */
            System.out.println(hello.sayHello("zhangxianxin"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意:要在远程对象上调用方法,RMI客户端首先必须从RMI注册表中检索远程存根对象。 此存根对象包含服务器地址,该服务器地址稍后将在调用远程方法时用于连接到远程对象(与RMI注册表的连接和与远程对象的连接是两个完全不同的东西)。 默认情况下,服务器将尝试检测自己的地址并将其传递给存根对象。 不幸的是,用于检测服务器地址的算法并不总是产生有用的结果(取决于网络配置:例如多网卡的环境)。通过设置RMI服务器上的系统属java.rmi.server.hostname,可以覆盖传递给存根对象的服务器地址。

方式一:Java代码实现

System.setProperty("java.rmi.server.hostname", "<<rmi server ip>>");

方式二:Java命令行参数

-Djava.rmi.server.hostname=<<rmi server ip>>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独行客-编码爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值