分布式对象

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

下面的图示说明了客户程序是如何调用分布式对象的:

从图上我们可以看到,分布式对象被调用的过程是这样的:

  1. 客户程序调用一个被称为 Stub (存根)的客户端代理对象。该代理对象负责对客户端隐藏网络通讯的细节。Stub 知道如何通过网络套接字(Socket)发送调用,包括如何将调用参数转换为适当的形式以便传输等。
  2. Stub 通过网络将调用传递到服务器端,也就是分布对象一端的一个被称为 Skeleton(骨干) 的代理对象。同样,该代理对象负责对分布式对象隐藏网络通讯的细节。Skeleton 知道如何从网络套接字(Socket)中接受调用,包括如何将调用参数从网络传输形式转换为 Java 形式等。
  3. Skeleton 将调用传递给分布式对象。分布式对象执行相应的调用,之后将返回值传递给 Skeleton,进而传递到 Stub,最终返回给客户程序。

这个场景基于一个基本的法则,即行为的定义和行为的具体实现相分离。如图所示,客户端代理对象 Stub 和分布式对象都实现了相同的接口,该接口称为远程接口(Remote Interface)。正是该接口定义了行为,而分布式对象本身则提供具体的实现。对于 Java RMI 而言,我们用接口(interface)定义行为,用类(class)定义实现。

RMI架构

RMI就是基于上述分布式原理的实现的,它的底层架构示意图如下:

  1. Stub/Skeleton 层:该层提供了客户程序和服务程序彼此交互的接口。
  2. 远程引用(Remote Reference)层:这一层相当于在其之上的 Stub/Skeleton 层和在其之下的传输协议层之前的中间件,负责处理远程对象引用的创建和管理。
  3. 传输协议(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的流程如下:

  1. 通过分析需求定义远程接口(客户端和服务器端公用的),此接口必须扩展java.rmi.Remote,且远程方法必须声明抛出 java.rmi.RemoteException 异常,或者该异常的超类(Superclass)。
  2. 服务器端实现远程接口,为了不手动生成stub需要继承UnicastRemoteObject类,并调用其构造器;
  3. 服务器端注册服务并启动;
  4. 客户端查询服务并调用远程方法;

示意图如下:

代码示例

分别建立三个项目:服务器端(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"));
}
}

运行

  1. 首先运行服务器端,切换到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

  2. 运行客户端,也先切换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原理及简单示例的更多相关文章

  1. Optaplanner规划引擎的工作原理及简单示例(2)

    开篇 在前面一篇关于规划引擎Optapalnner的文章里(Optaplanner规划引擎的工作原理及简单示例(1)),老农介绍了应用Optaplanner过程中需要掌握的一些基本概念,这些概念有且于 ...

  2. Optaplanner规划引擎的工作原理及简单示例(1)

    在之前的文章中,老猿已介绍过APS及规划的相关内容,也对Optaplanner相关的概念和一些使用示例进行过介绍,接下来的文章中,我会自己做一个规划小程序 - 一个关于把任务分配到不同的机台上进行作来 ...

  3. vue 实现原理及简单示例实现

    目录 相关html代码,用于被解析绑定数据 observer代码 Dep代码 Watcher 代码 Compile 代码 vue 简要构造函数 创建vue实例 结语 主要理解.实现如下方法: Obse ...

  4. Ajax -- 原理及简单示例

    1. 什么是Ajax •Ajax被认为是(AsynchronousJavaScript and XML的缩写).现在,允许浏览器与服务器通信而无须刷新当前页面的技术都被叫做Ajax. 2. Ajax ...

  5. RMI原理及简单demo

    1 简介 RMI是远程方法调用的简称,它能够帮助我们查找并执行远程对象的方法.通俗地说,远程调用就象将一个class放在A机器上,然后在B机器中调用这个class的方法. 2 概念 其他机器需要调用的 ...

  6. pureMVC简单示例及其原理讲解五(Facade)

    本节将讲述Facade,Proxy.Mediator.Command的统一管家.自定义Facade必须继承Facade,在本示例中自定义Facade名称为ApplicationFacade,这个名称也 ...

  7. pureMVC简单示例及其原理讲解四(Controller层)

    本节将讲述pureMVC示例中的Controller层. Controller层有以下文件组成: AddUserCommand.as DeleteUserCommand.as ModelPrepCom ...

  8. pureMVC简单示例及其原理讲解三(View层)

    本篇说的是View层,即视图层,在本示例中包括两个部分:MXML文件,即可视控件:Mediator. 可视控件 可视控件由UserForm.mxml(图1)和UserList.mxml(图2)两个文件 ...

  9. Websocket - Websocket原理(握手、解密、加密)、基于Python实现简单示例

    一.Websocket原理(握手.解密.加密) WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实 ...

随机推荐

  1. Angular - - ngInclude、ngTransclude

    这两个都是HTML DOM嵌入指令 ngInclude 读取,编译和插入外部的HTML片段. 格式:ng-include=“value”<ng-include src=”value” onloa ...

  2. endnote X7参考文献缩进设置

    首先打开[endnote]软件,在"edit-output styles-edit(你所选择的文献格式名称)"对话框中点击"bibliography-layout&quo ...

  3. Pomelo的component组件

    pomelo的核心是由一系列松耦合的component组成,同时我们也可以实现我们自己的component来完成一些自己定制的功能.对于我们的聊天应用,我们尝试给其增加一个component,目的是展 ...

  4. spring mvc 资源包的映射

    在springmvc.xml中进行设置: <?xml version="1.0" encoding="UTF-8"?> <beans xmln ...

  5. 使用Flex4容器若干技巧

    本文适用于正在寻找使用Flex 4容器和布局的快速参考指南的开发人员. 尽管这不一定是一个复杂问题,但这似乎是许多开发人员的挫折的来源,特别是对于那些Flex刚刚入门的开发人员. 当开发人员不知道如何 ...

  6. QJsonObject和QJsonArray的巨坑

    最近用Qt的QJsonObject和QJsonArray当做类变量来存储运行信息,发现这两货真的是巨坑.让人有一种JJ fly的感觉/(ㄒoㄒ)/~~. 写了个例子来说明下: MainWindow:: ...

  7. iOS开发中@property的属性weak nonatomic strong readonly等介绍

    @property与@synthesize是成对出现的,可以自动生成某个类成员变量的存取方法.在Xcode4.5以及以后的版本,@synthesize可以省略. 1.atomic与nonatomica ...

  8. Android 隐藏软键盘

    隐藏软键盘 public void hideSoftInputView() { InputMethodManager manager = ((InputMethodManager) this.getS ...

  9. MongoDB基础之九 replication复制集

    准备工作:创建目录 mkdir -p /home/m17 //home/m18 /home/m19 /home/mlog 1:启动3个实例,且声明实例属于某复制集 # ./bin/mongod --d ...

  10. 使用express.js框架一步步实现基本应用以及构建可扩展的web应用

    最近过年在家有点懈怠,但是自己也不断在学习新的前端技术,在家琢磨了express.js的web框架. 框架的作用就是提高开发效率,快速产出结果.即使不使用框架,我们也会在开发过程中逐渐形成构成框架. ...