每日扫盲(一):java的rmi
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的更多相关文章
- java回顾rmi
搞java的不懂rmi好像说不过去.. ,复习一遍. 参照http://www.iteye.com/topic/173909 http://lzj0470.iteye.com/blog/426760 ...
- java的RMI(Remote Method Invocation)
RMI 相关知识RMI全称是Remote Method Invocation-远程方法调用,Java RMI在JDK1.1中实现的,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网络 ...
- Java 使用RMI
Java 使用RMI Java使用序列化的方式,可以实现远端的方法调用,在分工合作时非常方便.本文记录使用java标准库实现rmi 一.服务端 结构 . ├── pom.xml ├── src │ ...
- Java实习生常规技术面试题每日十题Java基础(八)
目录 1.解释内存中的栈(stack).堆(heap)和静态区(static area)的用法. 2.怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串? 3.运行时异常与受检异常有 ...
- Java实习生常规技术面试题每日十题Java基础(七)
目录 1. Java设计模式有哪些? 2.GC是什么?为什么要有GC? 3. Java中是如何支持正则表达式. 4.比较一下Java和JavaSciprt. 5.Math.round(11.5) 等于 ...
- Java实习生常规技术面试题每日十题Java基础(六)
目录 1.在Java语言,怎么理解goto. 2.请描述一下Java 5有哪些新特性? 3.Java 6新特性有哪些. 4.Java 7 新特性有哪些. 5.Java 8 新特性有哪些. 6.描述Ja ...
- Java实习生常规技术面试题每日十题Java基础(五)
目录 1.启动一个线程是用run()还是start()? . 2.线程的基本状态以及状态之间的关系. 3.Set和List的区别,List和Map的区别? 4.同步方法.同步代码块区别? 5.描述Ja ...
- Java实习生常规技术面试题每日十题Java基础(四)
目录 1.String 和StringBuffer的区别. 2.数组有没有length()这个方法? String有没有length()这个方法? 3.final, finally, finalize ...
- Java实习生常规技术面试题每日十题Java基础(三)
目录 1.是否可以从一个static方法内部发出对非static方法的调用? 2.Integer与int的区别? 3.Overload和Override的区别.参数列表相同,返回值不同的方法,是否是重 ...
- Java实习生常规技术面试题每日十题Java基础(二)
目录 1. JAVA 的反射机制的原理. 2.静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同? 3.如何将String类型转化成Number类型. 4.什 ...
随机推荐
- Mabitis中的#与$符号区别及用法介绍
这篇文章主要介绍了Mabitis中的 #{}与 ${} 符号区别,需要的朋友可以参考下 一.介绍 mybatis 中使用 Mapper.xml里面的配置进行 sql 查询,经常需要动态传递参数,例 ...
- ubuntu Redis安装及配置
1.安装 1.1 下载压缩包:wget http://download.redis.io/releases/redis-5.0.4.tar.gz1.2 解压:tar xzf redis-5.0.4.t ...
- 零基础自学Python是看书还是看视频?
很多人都碍于Python培训班的高昂费用和有限的空余时间都选择自学Python,但是没有老师帮助,显得有些迷茫,不知应该从何处学起,也不知识看书学习还是应该看视频学习.本就来谈谈这个话题. 我们先 ...
- unity中添加音量控制的一些步骤
1.先确认要控制的音源(Audio Source)所使用的输出(Output),例如我这里BGM使用的是MainMixer: 2.暴露音量(Volume)参数,让脚本可以控制.这里如果不暴露出来,脚本 ...
- Tensorflow中one_hot() 函数用法
官网默认定义如下: one_hot(indices, depth, on_value=None, off_value=None, axis=None, dtype=None, name=None) 该 ...
- VS 2017 mscorlib.dll 加载元数据时发生严重错误,需要终止调试
VS 2017 mscorlib.dll 加载元数据时发生严重错误,需要终止调试 C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0. ...
- 爬取杭电oj所有题目
杭电oj并没有反爬 所以直接爬就好了 直接贴源码(参数可改,循环次数可改,存储路径可改) import requests from bs4 import BeautifulSoup import ti ...
- 一个小时学会jQuery(转载)
目录 一.jQuery简介与第一个jQuery程序 1.1.jQuery简介 1.2.jQuery特点 1.3.jQuery版本 1.4.获得jQuery库 1.5.第一个jQuery程序 二.jQu ...
- 在多租户(容器)数据库中如何创建PDB:方法5 DBCA远程克隆PDB
基于版本:19c (12.2.0.3) AskScuti 创建方法:DBCA静默远程克隆PDB.将 CDB1 中的 PDB1 克隆为 CDB2 中的 ERP2 对应路径:Creating a PDB ...
- <context:component-scan>标签
在spring-mvc的配置文件Springmvc-servlet.xml中,要扫描Controller注解的类,用<context:include-filter>标签 <conte ...