JAVA RMI 原理和使用浅析

本地对象调用

我们先看看本地对象方法的调用:

ObjectClass objectA = new ObjectClass();
String retn = objectA.Method();

但是想想,如果objectA对象在JVM a上;而我们的程序在JVM b上,而且想访问JVM a上的objectA对象方法,如何做呢?对于JVM b上的应用程序来说,是不知道JVM a上创建的ObjectClass实例对象名称是什么,因为这次我创建的实例对象可能是objectA,下次我程序一改,我创建的实例对象又叫objectB了,另外,我创没创建ObjectClass实例对象,JVM b上应用程序又怎么知道呢?

RMI就解决了这个问题。

工作原理

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

由于有RMI系统的支持,我们写RMI应用程序时只需要继承相关类,实现相关接口就可以了。也就是说,我们只需要定义接口、接口实现、客户端程序和服务端程序就可以了。

上图中的stub和skeleton代理都是在服务端程序中由RMI系统动态生成,服务端程序只需要继承java.rmi.server.UnicastRemoteObject类。

那么上图中的RMI Service(RMI registry)是怎么回事呢?

先卖个关子:

可以说,RMI由3个部分构成,第一个是RMIService即JDK提供的一个可以独立运行的程序(bin目录下的rmiregistry),第二个是RMIServer即我们自己编写的一个java项目,这个项目对外提供服务。第三个是RMIClient即我们自己编写的另外一个java项目,这个项目远程使用RMIServer提供的服务。
首先,RMIService必须先启动并开始监听对应的端口。
其次,RMIServer将自己提供的服务的实现类注册到RMIService上,并指定一个访问的路径(或者说名称)供RMIClient使用。
最后,RMIClient使用事先知道(或和RMIServer约定好)的路径(或名称)到RMIService上去寻找这个服务,并使用这个服务在本地的接口调用服务的具体方法。
通俗的讲完了再稍微技术的讲下:
首先,在一个JVM中启动rmiregistry服务,启动时可以指定服务监听的端口,也可以使用默认的端口。
其次,RMIServer在本地先实例化一个提供服务的实现类,然后通过RMI提供的Naming,Context,Registry等类的bind或rebind方法将刚才实例化好的实现类注册到RMIService上并对外暴露一个名称。
最后,RMIClient通过本地的接口和一个已知的名称(即RMIServer暴露出的名称)再使用RMI提供的Naming,Context,Registry等类的lookup方法从RMIService那拿到实现类。这样虽然本地没有这个类的实现类,但所有的方法都在接口里了,想怎么调就怎么调吧。
值得注意的是理论上讲RMIService,RMIServer,RMIClient可以部署到3个不同的JVM中,这个时候的执行的顺序是RMIService---RMIServer—RMIClient。另外也可以由RMIServer来启动RMIService这时候执行的顺序是RMIServer—RMIService—RMIClient。

实际应用中很少有单独提供一个RMIService服务器,开发的时候可以使用Registry类在RMIServer中启动RMIService。

  

RMI远程调用步骤
RMI的交互图:

RMI由3个部分构成,第一个是rmiregistry(JDK提供的一个可以独立运行的程序,在bin目录下),第二个是server端的程序,对外提供远程对象,第三个是client端的程序,想要调用远程对象的方法。
首先,先启动rmiregistry服务,启动时可以指定服务监听的端口,也可以使用默认的端口(1099)。
其次,server端在本地先实例化一个提供服务的实现类,然后通过RMI提供的Naming/Context/Registry(下面实例用的Registry)等类的bind或rebind方法将刚才实例化好的实现类注册到rmiregistry上并对外暴露一个名称。
最后,client端通过本地的接口和一个已知的名称(即rmiregistry暴露出的名称)再使用RMI提供的Naming/Context/Registry等类的lookup方法从RMIService那拿到实现类。这样虽然本地没有这个类的实现类,但所有的方法都在接口里了,便可以实现远程调用对象的方法了。

存根和骨干网的具体通信过程:

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

JAVA RMI简单示例

本示例是client端调用server端远程对象的加减法方法,具体步骤为:

1. 定义一个远程接口(这个是存在于server端的类)

import java.rmi.Remote;
import java.rmi.RemoteException; /**
* 必须继承Remote接口。
* 所有参数和返回类型必须序列化(因为要网络传输)。
* 任意远程对象都必须实现此接口。
* 只有远程接口中指定的方法可以被调用。
*/
public interface IRemoteMath extends Remote {    // 所有方法必须抛出RemoteException
public double add(double a, double b) throws RemoteException;
public double subtract(double a, double b) throws RemoteException; }

2、远程接口实现类(存在于服务器端计算机的类)

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import remote.IRemoteMath; /**
* 服务器端实现远程接口。
* 必须继承UnicastRemoteObject,以允许JVM创建远程的存根/代理。
*/
public class RemoteMath extends UnicastRemoteObject implements IRemoteMath { private int numberOfComputations; protected RemoteMath() throws RemoteException {
numberOfComputations = ;
} @Override
public double add(double a, double b) throws RemoteException {
numberOfComputations++;
System.out.println("Number of computations performed so far = "
+ numberOfComputations);
return (a+b);
} @Override
public double subtract(double a, double b) throws RemoteException {
numberOfComputations++;
System.out.println("Number of computations performed so far = "
+ numberOfComputations);
return (a-b);
} }

3、 服务器端)(存在于服务器端计算机上)

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import remote.IRemoteMath; /**
* 创建RemoteMath类的实例并在rmiregistry中注册。
*/
public class RMIServer { public static void main(String[] args) { try {
// 注册远程对象,向客户端提供远程对象服务。
// 远程对象是在远程服务上创建的,你无法确切地知道远程服务器上的对象的名称,
// 但是,将远程对象注册到RMI Registry之后,
// 客户端就可以通过RMI Registry请求到该远程服务对象的stub,
// 利用stub代理就可以访问远程服务对象了。
IRemoteMath remoteMath = new RemoteMath();
LocateRegistry.createRegistry();
Registry registry = LocateRegistry.getRegistry();
registry.bind("Compute", remoteMath);
System.out.println("Math server ready");
// 如果不想再让该对象被继续调用,使用下面一行
// UnicastRemoteObject.unexportObject(remoteMath, false);
} catch (Exception e) {
e.printStackTrace();
} } }

4. 客户端(存在于客户端计算机上)

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import remote.IRemoteMath; public class MathClient { public static void main(String[] args) { try {
// 如果RMI Registry就在本地机器上,URL就是:rmi://localhost:1099/hello
// 否则,URL就是:rmi://RMIService_IP:1099/hello
Registry registry = LocateRegistry.getRegistry("localhost");
// 从Registry中检索远程对象的存根/代理
IRemoteMath remoteMath = (IRemoteMath)registry.lookup("Compute");
// 调用远程对象的方法
double addResult = remoteMath.add(5.0, 3.0);
System.out.println("5.0 + 3.0 = " + addResult);
double subResult = remoteMath.subtract(5.0, 3.0);
System.out.println("5.0 - 3.0 = " + subResult);
}catch(Exception e) {
e.printStackTrace();
} } }

结果如下:

server端

client端

href:

https://blog.csdn.net/qq_28081453/article/details/83279066#JAVA_RMI_21

https://blog.csdn.net/xinghun_4/article/details/45787549

每日扫盲(一):java的rmi的更多相关文章

  1. java回顾rmi

    搞java的不懂rmi好像说不过去.. ,复习一遍. 参照http://www.iteye.com/topic/173909 http://lzj0470.iteye.com/blog/426760  ...

  2. java的RMI(Remote Method Invocation)

    RMI 相关知识RMI全称是Remote Method Invocation-远程方法调用,Java RMI在JDK1.1中实现的,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网络 ...

  3. Java 使用RMI

    Java 使用RMI Java使用序列化的方式,可以实现远端的方法调用,在分工合作时非常方便.本文记录使用java标准库实现rmi 一.服务端 结构 . ├── pom.xml ├── src │   ...

  4. Java实习生常规技术面试题每日十题Java基础(八)

    目录 1.解释内存中的栈(stack).堆(heap)和静态区(static area)的用法. 2.怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串? 3.运行时异常与受检异常有 ...

  5. Java实习生常规技术面试题每日十题Java基础(七)

    目录 1. Java设计模式有哪些? 2.GC是什么?为什么要有GC? 3. Java中是如何支持正则表达式. 4.比较一下Java和JavaSciprt. 5.Math.round(11.5) 等于 ...

  6. Java实习生常规技术面试题每日十题Java基础(六)

    目录 1.在Java语言,怎么理解goto. 2.请描述一下Java 5有哪些新特性? 3.Java 6新特性有哪些. 4.Java 7 新特性有哪些. 5.Java 8 新特性有哪些. 6.描述Ja ...

  7. Java实习生常规技术面试题每日十题Java基础(五)

    目录 1.启动一个线程是用run()还是start()? . 2.线程的基本状态以及状态之间的关系. 3.Set和List的区别,List和Map的区别? 4.同步方法.同步代码块区别? 5.描述Ja ...

  8. Java实习生常规技术面试题每日十题Java基础(四)

    目录 1.String 和StringBuffer的区别. 2.数组有没有length()这个方法? String有没有length()这个方法? 3.final, finally, finalize ...

  9. Java实习生常规技术面试题每日十题Java基础(三)

    目录 1.是否可以从一个static方法内部发出对非static方法的调用? 2.Integer与int的区别? 3.Overload和Override的区别.参数列表相同,返回值不同的方法,是否是重 ...

  10. Java实习生常规技术面试题每日十题Java基础(二)

    目录 1. JAVA 的反射机制的原理. 2.静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同? 3.如何将String类型转化成Number类型. 4.什 ...

随机推荐

  1. Linux安装Tomcat,解决不能访问Manager App

      在Windows环境中安装Tomcat时,只需要在Tomcat目录下/conf/tomcat-user.xml文件增加用户就可以了.在Linux系统中添加了username还是不能访问. 一.Li ...

  2. springboot整合websocket实现客户端与服务端通信

    定义  WebSocket是通过单个TCP连接提供全双工(双向通信)通信信道的计算机通信协议.此WebSocket API可在用户的浏览器和服务器之间进行双向通信.用户可以向服务器发送消息并接收事件驱 ...

  3. Date/Math/String对象的函数

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. Java单体应用 - 架构模式 - 02.MVC架构

    原文地址:http://www.work100.net/training/monolithic-architecture-mvc.html 更多教程:光束云 - 免费课程 MVC架构 序号 文内章节 ...

  5. QT版本

    最近在linux下安装qt:发现主要的问题是qt的版本问题:下面来谈谈各个版本的理解 Qt 的版本是按照不同的图形系统来划分的,目前分为五个版本: Win: 适用于Miccrosoft Windows ...

  6. 并查集路径减半优化 UnionFind PathHalving (C++)

    /* * UnionFind.h * 有两种实现方式,QuickFind和QuickUnion * QuickFind: * 查找O(1) * 合并O(n) * QuickUnion:(建议使用) * ...

  7. Oracle用户权限授权以及索引、同义词、分区

    本文为原创,如需转载,请标明出处 http://www.cnblogs.com/gudu1/p/7601765.html ---- 用户权限 1.创建表空间 (创建用户之前需要创建表空间和临时表空间, ...

  8. 持续集成与自动化部署---代码流水线管理及Jenkins和gitlab集成

    1.代码流水线管理 Pipeline名词顾名思义就是流水线的意思,因为公司可能会有很多项目.如果使用jenkins构建完成后,开发构建项目需要一项一项点击,比较麻烦.所以出现pipeline名词. 代 ...

  9. 参考 ZTree 加载大数据量。加载慢问题解析

    参考 ZTree 加载大数据量. 1.一次性加载大数据量加载说明 1).zTree v3.x 针对大数据量一次性加载进行了更深入的优化,实现了延迟加载功能,即不展开的节点不创建子节点的 DOM. 2) ...

  10. Js script type="text/template"的使用简单说明

    <script type="text/template" id="treeTableTpl"> <tr id="{{row.id}} ...