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. C语言回文链表

    要求:请判断一个链表是否为回文链表. 示例 1: 输入: 1->2 输出: false 示例 2: 输入: 1->2->2->1 输出: true思路:利用快慢双指针+反转半链 ...

  2. HDU1241 Oil Deposits(dfs+连通块问题)

    背景描述 ztw同志负责探测地下石油储藏.ztw现在在一块矩形区域探测石油.他通过专业设备,来分析每个小块中是否蕴藏石油.如果这些蕴藏石油的小方格相邻(横向相邻,纵向相邻,还有对角相邻),那么它们被认 ...

  3. Python tip

    shutil.rmtree() 表示递归删除文件夹下的所有子文件夹和子文件.

  4. Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) C Messy

    //因为可以反转n次 所以可以得到任何可以构成的序列 #include<iostream> #include<string> #include<vector> us ...

  5. linux添加新的环境变量

    Linux下设置PYTHONPATH环境变量有三种方法:一种作用于当前终端,一种作用于当前用户,一种作用于所有用户. 1.作用于当前终端,直接当前终端输入命令 $ export PYTHONPATH= ...

  6. python2下解决json的unicode编码问题

    基础知识:   序列化——json.dumps()函数是将一个Python数据类型列表进行json格式的编码(可以这么理解,json.dumps()函数是将字典转化为json字符串)   反序列化—— ...

  7. V8 是怎么跑起来的 —— V8 中的对象表示

    V8 是怎么跑起来的 —— V8 中的对象表示 ThornWu The best is yet to come 30 人赞同了该文章 本文创作于 2019-04-30,2019-12-20 迁移至此本 ...

  8. java继承与多态课后作业

    1.动手实验 源码 class Grandparent {     public Grandparent()  {          System.out.println("GrandPar ...

  9. laydate 限制结束日期不能大于起始日期

    时间选择器在选择的时候,同时配置了另一个时间选择器内的参数 <div class="form-group"> <label for="exampleIn ...

  10. (转)Hadoop Combiner

    转自:http://blog.csdn.net/jokes000/article/details/7072963 众所周知,Hadoop框架使用Mapper将数据处理成一个<key,value& ...