RMI原理及简单示例
分布式对象
在学习 RMI 之前,先来分布式对象(Distributed Object):分布式对象是指一个对象可以被远程系统所调用。对于 Java 而言,即对象不仅可以被同一虚拟机中的其他客户程序(Client)调用,也可以被运行于其他虚拟机中的客户程序调用,甚至可以通过网络被其他远程主机之上的客户程序调用。
下面的图示说明了客户程序是如何调用分布式对象的:

从图上我们可以看到,分布式对象被调用的过程是这样的:
- 客户程序调用一个被称为 Stub (存根)的客户端代理对象。该代理对象负责对客户端隐藏网络通讯的细节。Stub 知道如何通过网络套接字(Socket)发送调用,包括如何将调用参数转换为适当的形式以便传输等。
- Stub 通过网络将调用传递到服务器端,也就是分布对象一端的一个被称为 Skeleton(骨干) 的代理对象。同样,该代理对象负责对分布式对象隐藏网络通讯的细节。Skeleton 知道如何从网络套接字(Socket)中接受调用,包括如何将调用参数从网络传输形式转换为 Java 形式等。
- Skeleton 将调用传递给分布式对象。分布式对象执行相应的调用,之后将返回值传递给 Skeleton,进而传递到 Stub,最终返回给客户程序。
这个场景基于一个基本的法则,即行为的定义和行为的具体实现相分离。如图所示,客户端代理对象 Stub 和分布式对象都实现了相同的接口,该接口称为远程接口(Remote Interface)。正是该接口定义了行为,而分布式对象本身则提供具体的实现。对于 Java RMI 而言,我们用接口(interface)定义行为,用类(class)定义实现。
RMI架构
RMI就是基于上述分布式原理的实现的,它的底层架构示意图如下:

- Stub/Skeleton 层:该层提供了客户程序和服务程序彼此交互的接口。
- 远程引用(Remote Reference)层:这一层相当于在其之上的 Stub/Skeleton 层和在其之下的传输协议层之前的中间件,负责处理远程对象引用的创建和管理。
- 传输协议(Transport Protocol) 层:该层提供了数据协议,用以通过线路传输客户程序和远程对象间的请求和应答。
Java RMI 的客户程序使用客户端的 Stub 向远程对象请求方法调用;服务器对象则通过服务器端的 Skeleton 接受请求。
在 Java 1.2 之后,RMI 不再需要 Skeleton 对象,而是通过 Java 的反射机制(Reflection)来完成对服务器端的远程对象的调用;而在Java 1.5之后,使用了UnicastRemoteObject类就可以不需要手动生成stub了,而是由Proxy动态生成,客户端从服务器端下载stub后装载。
整个调用过程如下:客户端从服务器端下载stub到本地,stub把客户端的参数序列化后,传至远程引用层;远程引用层根据RMI协议转换为传输层数据,通过传输层把数据传到服务器端;服务器端接收到从传输层传过来的数据,通过远程引用层通过RMI协议进行转换,Skeleton 把参数反序列化后传递给服务器端的方法调用。如果方法调用产生异常或返回值再经由Skeleton 序列化给客户端,客户端再反序列化。(序列化可参考这里。)
创建RMI流程
建立RMI的流程如下:
- 通过分析需求定义远程接口(客户端和服务器端公用的),此接口必须扩展java.rmi.Remote,且远程方法必须声明抛出 java.rmi.RemoteException 异常,或者该异常的超类(Superclass)。
- 服务器端实现远程接口,为了不手动生成stub需要继承UnicastRemoteObject类,并调用其构造器;
- 服务器端注册服务并启动;
- 客户端查询服务并调用远程方法;
示意图如下:

代码示例
分别建立三个项目:服务器端(DemoRMI.Server)、客户端(DemoRMI.Client)和远程接口(DemoRMI.RmoteInterface),如下图:

DemoRMI.RmoteInterface中的IHello.java
package net.oseye.DemoRMI.RmoteInterface; import java.rmi.Remote;
import java.rmi.RemoteException; /**
* 远程接口
* @author oseye
*/
public interface IHello extends Remote{ String sayHello(String name) throws RemoteException;
}
DemoRMI.Server中的HelloImpl.java
package net.oseye.DemoRMI.Server; import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject; import net.oseye.DemoRMI.RmoteInterface.IHello; @SuppressWarnings("serial")
public class HelloImpl extends UnicastRemoteObject implements IHello { protected HelloImpl() throws RemoteException {
super();
} public String sayHello(String name) {
return "Hello,"+name;
}
}
DemoRMI.Server中的App.java
package net.oseye.DemoRMI.Server; import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry; import net.oseye.DemoRMI.RmoteInterface.IHello; /**
* 服务器端
* @author oseye
*/
public class App
{
public static void main( String[] args ) throws RemoteException, MalformedURLException
{
IHello hello=new HelloImpl(); LocateRegistry.createRegistry(1099);
Naming.rebind("rmi://10.9.146.113/hello", hello);
System.out.println("ok");
}
}
DemoRMI.Client中的App.java:
package net.oseye.DemoRMI.Client; import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException; import net.oseye.DemoRMI.RmoteInterface.IHello; /**
* RMI客户端
* @author oseye
*/
public class App
{
public static void main( String[] args ) throws MalformedURLException, RemoteException, NotBoundException
{
IHello hello=(IHello) Naming.lookup("rmi://10.9.146.113/hello");
System.out.println( hello.sayHello("oseye"));
}
}
运行
首先运行服务器端,切换到classes目录:
cd /D D:\workspace\DemoRMI.Server\target\classes
执行服务器端:
java -cp .;D:/workspace/DemoRMI.RmoteInterface/target/classes net.oseye.DemoRMI.Server.App
PS:如果你嫌使用classpath麻烦,可以打个包执行。
输出
ok
- 运行客户端,也先切换classes目录:
cd /D D:\workspace\DemoRMI.Client\target\classes
执行客户端:
java -cp .;D:\workspace\DemoRMI.RmoteInterface\target\classes net.oseye.DemoRMI.Client.App
输出
Hello,oseye
RMI原理及简单示例的更多相关文章
- Optaplanner规划引擎的工作原理及简单示例(2)
开篇 在前面一篇关于规划引擎Optapalnner的文章里(Optaplanner规划引擎的工作原理及简单示例(1)),老农介绍了应用Optaplanner过程中需要掌握的一些基本概念,这些概念有且于 ...
- Optaplanner规划引擎的工作原理及简单示例(1)
在之前的文章中,老猿已介绍过APS及规划的相关内容,也对Optaplanner相关的概念和一些使用示例进行过介绍,接下来的文章中,我会自己做一个规划小程序 - 一个关于把任务分配到不同的机台上进行作来 ...
- vue 实现原理及简单示例实现
目录 相关html代码,用于被解析绑定数据 observer代码 Dep代码 Watcher 代码 Compile 代码 vue 简要构造函数 创建vue实例 结语 主要理解.实现如下方法: Obse ...
- Ajax -- 原理及简单示例
1. 什么是Ajax •Ajax被认为是(AsynchronousJavaScript and XML的缩写).现在,允许浏览器与服务器通信而无须刷新当前页面的技术都被叫做Ajax. 2. Ajax ...
- RMI原理及简单demo
1 简介 RMI是远程方法调用的简称,它能够帮助我们查找并执行远程对象的方法.通俗地说,远程调用就象将一个class放在A机器上,然后在B机器中调用这个class的方法. 2 概念 其他机器需要调用的 ...
- pureMVC简单示例及其原理讲解五(Facade)
本节将讲述Facade,Proxy.Mediator.Command的统一管家.自定义Facade必须继承Facade,在本示例中自定义Facade名称为ApplicationFacade,这个名称也 ...
- pureMVC简单示例及其原理讲解四(Controller层)
本节将讲述pureMVC示例中的Controller层. Controller层有以下文件组成: AddUserCommand.as DeleteUserCommand.as ModelPrepCom ...
- pureMVC简单示例及其原理讲解三(View层)
本篇说的是View层,即视图层,在本示例中包括两个部分:MXML文件,即可视控件:Mediator. 可视控件 可视控件由UserForm.mxml(图1)和UserList.mxml(图2)两个文件 ...
- Websocket - Websocket原理(握手、解密、加密)、基于Python实现简单示例
一.Websocket原理(握手.解密.加密) WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实 ...
随机推荐
- jQuery 的 ready 函数是如何工作的?(源码分析)
如果你使用过 jQuery , 就必然使用过 ready 函数,它用来注册当页面准备好之后可以执行的函数. 问题来啦,我们的页面什么时候准备好了呢? 1. onload 事件 最基本的处理方式就是页面 ...
- 解决Centos 7 下 tomcat字体异常 Font '宋体' is not available to the JVM
错误提示: SEVERE: Servlet.service() for servlet [example] in context with path [/myproject] threw except ...
- JavaScript成员属性读取
var obj = {}; 检索一个不存在的成员属性的值,将返回undefined; 可以使用||运算符来填充默认值: var status = obj.status||'inistatus' 从un ...
- 微信LazyMan笔试题的深入解析和实现
一.题目介绍 以下是我copy自网上的面试题原文: 实现一个LazyMan,可以按照以下方式调用: LazyMan("Hank")输出: Hi! This is Hank! ...
- 关于WIN10开机无法输入密码的问题
昨日,电脑 遇到了开机无法输入密码的问题,神烦. 作为一个计算狗,怎么能直接装系统(百度了一堆方法,装系统,果真万能)呢. 所以,深刻的分析了下. 1 .首先说明基本情况. 计算机品牌:ASUS 系统 ...
- TimSort in Java 8
在项目中使用了Collections.sort(list, comparator)对集合进行了排序,偶然间遇到异常IllegalArgumentException: "Comparison ...
- 【福利大放送】不止是Android,Github超高影响力开源大放送,学习开发必备教科书
一.写在前面 最近项目重构,时间贼多,也没什么时间更新博客,个人的开源项目也是多时没有更新了:github地址,然而没有更新不代表我不在乎,后面一有空还是会继续提交的. 还是来冒个泡,给大家献上一些福 ...
- Java内部类之匿名内部类
我们都知道Java中可以使用内部类,将一个类的定义放在另一个类的定义的内部,这就是内部类,但是匿名内部类往往使我们摸不着头脑,因为它并没有特定的名称,那么该如何使用它呢? 定义一个匿名内部类 pu ...
- java 文件操作 写入和读取(小结一)
参考了这篇博客并优化,谢谢:http://blog.sina.com.cn/s/blog_99201d890101b4le.html 功能: 实现通过两个类完成先写入文件,再读取数据计算显示 pac ...
- IP数据报格式 及路由转发算法
ip数据报分首部和数据两部分组成: 首部分为固定部分和可变部分 版本--占 4 位,指 IP 协议的版本 目前的 IP 协议版本号为 4 (即 IPv4) 首部长度--占 4 位,可表示的最大数值 是 ...