RMI 框架工作原理(附源码)

RMI 框架

应用在分布式开发。

在RMI中,首先服务器端的某个类要 extends java.rmi.Romote ,然后在 RMI 注册器中注册远程对象,客户端通过代理访问 RMI 注册器,查询远程对象,最后才能调用服务器上的远程对象。

详细过程

  • HelloService 接口要 extends Remote 接口,那么这类(或接口)就可以被远程访问

  • 然后在将 HelloService 通过 HelloServiceImpl 类实现,然后实例化对象 ser1,将对象在 RMI上注册并重命名为 HelloService1

     //实例化对象
     HelloService ser1 = new HelloServiceImpl("ser1");
     
     //注册对象
     registry.rebind("HelloService1",ser1);
    
  • 在客户端实现 HelloService 接口,然后就可以调用远程对象 HelloService1

     HelloService service1 = (HelloService) registry.lookup("HelloService1");
     
     System.out.println(service1.echo("远程调用成功"));
    

1.RMI框架出现的背景

RMI 协议(Remote Method Protocol):远程方法调用。在JAVA 中,只要一个类(或接口) extends了 java.rmi.Remote接口,这个类就可以被客户端远程访问,并提供服务。

RMI 框架封装了所有底层通信细节,并且解决了编组,分布式垃圾收集。安全检查和并发性等通用问题。有了现成的框架。

分布式对象模型

通信过程

远程对象:如果一个对象不仅能够被本地进程访问,还能被其他主机进程访问,那么这个对象就是远程对象。一般远程对象都在服务器端,能提供各种服务,客户端则访问服务器端的远程对象,请求特定的服务

对象模型的实现系统都应该具备以下功能:

  1. 把分布在不同节点上的对象之间发送的消息转为字节序列,这一过程称为编组(marshalling)
  2. 通过套接字建立连接并发送编组后的消息,即字节序列
  3. 处理网络连接或传输消息是出现的各种障碍
  4. 为分布在不同节点上的对象提供分布式垃圾收集机制
  5. 为远程方法调用提供安全检查机制
  6. 服务端运用多线程或非阻塞通信机制,确保远程对象具有很好的并发性能,能同时被多个客户访问
  7. 创建与特定问题领域相关的各种本地对象和远程对象

总结一句话就是:分布式系统要处理套接字,编组,分布式垃圾收集,安全检查和并发性等问题,还要开发各种本地对象和远程对象

常见分布式框架

  • RMI

  • CORBA (公共对象请求代理体系结构):分布式对象模型的通用框架,允许用不用语言的对象进行通信,但是这一技术不再流行。

  • SOAP (Simple Object Access Protocol,简单对象访问协议):允许异构的系统之间能彼此通信,以XML作为通信语言。一个系统可以访问另一个系统对外公布的web服务

2.RMI 基本原理

RMI 采用客户/服务器通信方式。在服务器上部署了提供各种服务器的远程对象,客户端请求访问服务器上远程对象的方法。

客户端请求调用远程对象的方法

如图 HelloServiceImpl 是一个远程对象,它运行在服务器上,客户端请求调用 HelloServiceImpl对象的 echo() 方法

在这里插入图片描述

RMI 框架采用代理来封装通信细节

在这里插入图片描述

客户端和服务器端都会产生代理

  • 客户端的代理被称为远程对象的存根,通过存根调用相关方法,因为存根也会实现HelloService 接口
  • 服务器端的代理被称为远程对象的骨架。骨架类没有实现 HelloService 远程接口,HelloServiceImpl 实现了 HelloService 接口。严格上讲,骨架类不是代理类。代理类与被代理类实现的接口要一致
  • 存根和骨架就会通过套接字进行通信,发送被编组的参数

存根发送的内容

  • 被访问的远程对象的名字
  • 被调用的方法的描述
  • 编组后的参数的字节序列

服务器端接收到客户端的请求,骨架进行以下操作

  • 反编组参数
  • 定位要访问的远程对象
  • 调用远程对象的相应方法
  • 获取调用产生的值或异常,对其进行编组
  • 把编组后的返回值或者异常发给客户

3.创建RMI 应用流程(文章最后有源码详解)

  1. 创建远程接口:继承 Java.rmi.Remote 接口
  2. 创建远程类:实现远程接口
  3. 创建服务器程序:负责在 RMI 注册器中注册远程对象
  4. 创建客户程序:负责定位远程对象,并且调用远程对象的方法

问题思考

  1. 为什么要实现 HelloService 接口

    客户端也要实现 HelloService ,作为存根类,与服务器端进行通信。没有这个接口,客户端与服务端无法通信

参考文献

bilibili视频:https://www.bilibili.com/video/BV18N411o7cA

源代码

HelloService.java

package com;

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Date;
/*
创建远程接口
接口HelloService 是可以随便命名的,只是习惯用HelloService
 */

/*
注意这是一个接口,创建方法时 和类的写法不太一样
 */

public interface HelloService extends Remote{
    public String echo(String msg) throws RemoteException;
    public Date getTime() throws RemoteException;

}

HelloServiceImpl.java

package com;

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

/*
创建远程类。运行在服务器上
远程类就是远程对象所属的类。RMI规范要求远程类必须实现一个远程接口。
此外,为了使远程类的实例 变成能为远程客户提供服务 的远程对象,可以通过继承 java.rmi.server.UnicastRemoteObject
 */

public class HelloServiceImpl extends UnicastRemoteObject implements HelloService {

    private String name;

    protected HelloServiceImpl(String name) throws RemoteException {
        this.name=name;
    }

    public String echo(String msg) throws RuntimeException{
        System.out.println(name+"调用echo()");
        return "echo:"+msg+" from " +name;
    }

    public Date getTime() throws RuntimeException{
        System.out.println(name+"调用getTime()");
        Date d = new Date();
        return d;
    }

}

ServicePro.java

package com;

import java.rmi.*;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

/*
RMI 采用一种 命名服务机制 来使得客户可以找到服务器上的一个远程对象。
RMI 注册器会提供这种命名服务
注册器就像日常生活中的114电话查询系统,如果某单位想把自己的电话公开,在114上面注册认证就可以了

启动RMI注册器的两种方式
    1.直接运行 JDK 的bin目录下载的 rmiregistry.exe 程序
    2.调用 java.rmi.registry.LocateRegistry 类的静态方法 createRegistry()
 */

/*
服务器的对象要想能被客户端远程调用,必须注册远程对象。
    1.调用 java.rmi.registry.Registry 接口的 bind() 和 rebind()
    2.除了 java.rmi.registry.Registry; 还有java.rmi.registry.Naming;也是bind() 和 rebind()
    3.调用 JNDI API(JAVA Naming and Directory Interface,java名字与目录接口)
 */
public class ServicePro {

        /*
        Registry registry = new Registry() {
            @Override     //lookup 是查找对象,返回与参数name指定的名字所绑定的对象
            public Remote lookup(String name) throws RemoteException, NotBoundException, AccessException {
                return null;
            }

            @Override       //取消对象与名字的绑定
            public void bind(String name, Remote obj) throws RemoteException, AlreadyBoundException, AccessException {

            }

            @Override
            public void unbind(String name) throws RemoteException, NotBoundException, AccessException {

            }

            @Override
            public void rebind(String name, Remote obj) throws RemoteException, AccessException {

            }

            @Override
            public String[] list() throws RemoteException, AccessException {
                return new String[0];
            }
        };

         */
        public static void main(String args[]){
            try {
                //实例化两个远程对象,由于接口不能实例化,因此只能通过类实现接口,在通过类进行实例化
                HelloService ser1 = new HelloServiceImpl("ser1");
                HelloService ser2 = new HelloServiceImpl("ser2");

                //创建并启动注册器,监听1099端口
                Registry registry = LocateRegistry.createRegistry(1099);

                //注册远程对象,将ser1 命名为 HelloService1
                registry.rebind("HelloService1",ser1);
                registry.rebind("HelloService2",ser2);

                System.out.println("Success Registry Two Objects");

            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }
}

Client.java

package com;
/*
客户端程序
要远程访问服务器上的对象(这些对象在rmi注册器上已经注册)
 */

import sun.rmi.registry.RegistryImpl;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Client {
    //列出所有对象,肯定要存在一个数组中
    public static void showRemoteObject(Registry registry) throws RemoteException {
        String[] names = registry.list();           //创建字符串数组
        for(String name:names){
            System.out.println(name);
        }
    }

    public static void main(String agrs[]) throws RemoteException, NotBoundException {
        //客户端请求连接服务器端的1099端口
        Registry registry = LocateRegistry.getRegistry(1099);

        //实现接口,也就是实现存根类(相当于客户端的代理)
        HelloService service1 = (HelloService) registry.lookup("HelloService1");
        HelloService service2 = (HelloService) registry.lookup("HelloService2");

        //测试存根对象所属的类
        Class stuClass = service1.getClass();
        System.out.println("service1 是"+stuClass.getName()+"的实例");
        // 继承了 Remote接口的 HelloService接口,被远程调用成功,证明HelloService提供了远程服务
        System.out.println(service1.echo("远程调用成功"));

    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Ehcache RMI 提供了一种自动化的远程方法调用(RMI)功能,它能够将 Ehcache 缓存的数据通过远程方法调用进行访问和操作。 Ehcache 是一个流行的 Java 缓存框架,用于提升应用程序的性能和响应速度。它能够在内存中缓存数据,减少对数据库或其他外部资源的频繁访问。然而,当多个应用程序运行在不同的服务器上,且它们之间需要共享和访问缓存数据时,就需要一种能够在不同服务器之间进行数据传输和方法调用的机制。 Ehcache RMI 提供了这样的机制。它通过 Java 的远程方法调用技术,将 Ehcache 缓存的数据和方法暴露给远程服务器。这样,远程服务器就能够通过网络访问 Ehcache 缓存数据,执行方法操作,而无需直接访问原始的数据库或其他资源。 Ehcache RMI 的自动化特性指的是,它能够自动处理远程方法调用的细节,如序列化和反序列化对象、传输数据、调用方法等。开发人员只需配置正确的网络地址和端口,并提供需要远程访问和操作的缓存数据和方法,Ehcache RMI 就会自动完成数据传输和方法调用的过程。 总结来说,Ehcache RMI 是一种能够自动化实现远程方法调用的机制,它利用 Java RMI 技术,通过网络访问和操作 Ehcache 缓存数据。开发人员只需进行简单的配置,Ehcache RMI 就能够自动处理远程方法调用的细节,方便实现多个应用程序之间的数据共享和访问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Buffedon

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

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

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

打赏作者

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

抵扣说明:

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

余额充值