每日扫盲(一):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.什 ...
随机推荐
- PHP实现推送微信小程序模板消息
这边只会写如何实现,至于在公众号管理后台添加模板消息可以参考这篇文章: https://www.cnblogs.com/txw1958/p/wechat-template-message.html,当 ...
- 我的翻译--GSMem:通过GSM频率从被物理隔离的计算机上窃取数据
抽象概念 AG网络是指在物理上与公共互联网断开的网络.虽然近几年人们验证了入侵这类网络系统的可行性,但是从这种网络上获取数据仍然是一个有挑战的任务.在本文中,我们介绍GSMem,它是一个可以在蜂窝数据 ...
- JS全选按钮练习
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...
- 2019-08-09 纪中NOIP模拟B组
T1 [JZOJ1035] 粉刷匠 题目描述 windy有N条木板需要被粉刷. 每条木板被分为M个格子. 每个格子要被刷成红色或蓝色. windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一 ...
- HttpApplication处理管道处理过程简单描述
HttpApplication对象是由Asp.net帮助我们创建的,它是asp.net中处理请求的重要对象.为了便于扩展,HttpApplication采用处理管道的方式进行处理,将处理的步骤分为多个 ...
- mysql对表中数据根据某一字段去重
要删除重复的记录,就要先查出重复的记录,这个很容易做到 注意:这是查出所有重复记录的第一条记录,需要保留,因此需要添加查询条件,查出所有的重复记录 ) ) 然后 delete from cqssc w ...
- 《Mongo权威指南》学习手记
1.ObjectId: 是“_id”的默认类型.mongo没有用自增主键原因:多个服务器同步自动增加主键值费时费力. mongo初衷是作分布式数据库,所以能在分片环境中生成唯一的标示符非常重要. Ob ...
- Sql 语句常语法
以前感觉在这个方面很欠缺,于是就找了些这方面的材料,自己也做了些总结,汇总到了一块.便于以后的查阅. --1.获取表的主键字段SELECT name FROM SysColumns WHERE id= ...
- Laravel-admin 刷新页面后,左侧菜单栏怎么默认选中当前栏目
修改public\vendor\laravel-admin\laravel-admin里面的laravel-admin.js $(window).on('load',function(){ $('.s ...
- MyBatis使用步骤及原理
主要讲解MyBatis-基本使用步骤 回顾: MyBatis是一个数据持久层(ORM)框架.把实体 类和SQL语句之间建立了映射关系,是一种半自 动化的ORM实现.MyBATIS需要 ...