RMI(Remote Method Invocation)原理浅析

原创 2008年11月11日 20:57:00

博采众生现代的 网络和编程技术,让我们的资源共享从信息逐步迈向了硬件,你想到过吗?你可以把一个解密的任务放到远程的运行更快的,闲置的服务器上进行,最后把运算的结果返回来,你 所要做的就是提交任务。看到了吧,处理这些任务是远程的服务器。

Java的RMI的技术让这种实现变得非常的容易,本文就从基础性的东西讲解一下RMI的原理,有关安全和部署的问题,本文不加于讨论。 读过本文希望对你的学习有所帮助。

声明:未经允许,请勿转载本文,谢谢!

本文将讨论以下内容:

一、RMI的定义 1、RMI定义和功能 2、Stub和Skeleton介绍 二、RMI原理的浅析 1、RMI的应用开发流程 2、Stub和Skeleton在什么位置产生 3、Stub和公共接口(远程对象的功能)的关系 4、rmiregistry介绍 三、RMI简单实现

一、RMI的定义

1、RMI定义和功能

RMI是Remote Method Invocation的简称,是J2SE的一部分,能够让程序员开发出基于Java的分布式应用。一个RMI对象是一个远 程Java对象,可以从另一个Java虚拟机上(甚至跨过网络)调用它的方法,可以像调用本地Java对象的方法一样调用远程对象的方法,使分布在不同的JVM中的对象的外表和行为都 像本地对象一样。

2、Stub和Skeleton介绍

在学习RMI的时,我们不能不讨论stub和skeleton作用和相关问题。他们是我们理解RMI原理的关键。我做个比方说明这两个概念。 假如你是A,你想借D的工具,但是又不认识D的管家C,所以你找来B来帮你,B认识C。B在这时就是一个代理,代理你的请求,依靠自己的话语去借。C呢他负责D家东西收回和借出 ,但是要有D的批准。在得到D的批准以后,C再把东西给B,B呢再转给A。stub和skeleton在RMI中就是角色就是B和C,他们都是代理角色,在现实开发中隐藏系统和网络的的差异, 这一部分的功能在RMI开发中对程序员是透明的。Stub为客户端编码远程命令并把他们发送到服务器。而Skeleton则是把远程命令解码,调用服务端的远程对象的方法,把结果在编 码发给stub,然后stub再解码返回调用结果给客户端。所有的操作如下图所示:

博采众生

二、RMI原理的浅析

1、RMI的应用开发流程

为了方便理解,在这里我先介绍RMI应用开发的流程。如果你在网上搜索RMI,你会看到好多有关RMI的经典例子。在这里需要说明的就是 从JDK5.0以后,stubs和skeleton的类的产生不需要使用单独的rmic编译器了,所以本文开发流程就简化为客户端和服务端,没有中间的stub和skeleton的环节。整个开发流程如下 图:

博采众生

公共接口是客户端和服务端共同存在的,定义了远程对象将要实现的功能,客户端依这个接口调用的方法,在服务端都必须实现。随后 我们要开发实现接口的,继承java.rmi.UnicastRemoteObject的实现类,暂且把其称为Impl,这个类我们将在随后的服务端类中实例化并把其注册到RMI注册表中。运行RMI注册表 服务,具体各个平台的启动方法,请你参考JDK文档。而余下的问题就是开发服务端类和客户端类了,在服务类中,需要实例化Impl,并用一个字符串把这个对象绑定到RMI注册表 中;客户端需要实现检索RMI注册表获得需要远程对象的stub,进行远程方法调用。关于如何的实现,请网友看本文的RMI简单实现部分。

2、Stub和Skeleton在什么位置产生

网上有太多的stub和skeleton的疑问了,对stub和skeleton迷惑影响了对RMI的学习。从JDK5.0以后,这两个类就不需要rmic来产生了 ,而是有JVM自动处理,实际上他们还是存在的。Stub存在于客户端,作为客户端的代理,让我们总是认为客户端产生了stub,接口没有作用。实际上stub类是通过Java动态类下载 机制下载的(具体内容请参考:Java RMI实现代码动态下载),它是由服 务端产生,然后根据需要动态的加载到客户端,如果下次再运行这个客户端该存根类存在于classpath中,它就不需要再下载了,而是直接加载。(具体的内部细节,需要参考Sun 的Rmi - Java Remote Method Invocation – Specification)。总的来说,stub是在服务端产生的,如果服务端的stub内容改变,那么客户端的也是需要同步更新。

3、Stub和公共接口interface(远程对象的功能)的关系

我的一篇译文上,一个网友说 “如果客户端有接口的话,我还要那个存根来干什么??直接用接口不就完事了?经过测试,客户端放一个接口的话,根本不需要存根,这个是我一直想不明白的..........”。在开始 学习时,我也是有这个疑问,通过接口直接调用不就OK了吗?实际上,存根是我们必需的。一个服务如果没有合适的存根类,客户就没有办法去调用远程的接口,RMI使用存根来返 回引用远程对象接口的参数。在类的关系中,接口是不能实例化的,但是它可以指向一个实现该接口的实例。存根和接口就是这种关系,而存根类的实例就是在:lookup ()方法 调用时加载、实例的。接口只是告诉JVM,内存中这片的字节码中,这几个方法我可以调用。

4、rmiregistry介绍

我们用一个图示来说明rmiregistry的作用:

博采众生

Rmiregistry需要在提供远程对象服务端启动,它提供了一个环境,说白了就是在内存中,开辟了一片空间,用来接受服务端服务程序的 注册,产生一个类似于数据库,提供存储检索远程对象功能的注册表。这个RMI注册表,就是我们常听到的RMI名字服务。远程客户端,就是依靠它获得存根,调用远程方法。说到 这里有一个端口的问题,如果你在启动rmiregistry时,设定了非默认端口,那么需要在服务端和客户端统一使用该端口,否则就会有RemoteException的异常抛出,rmiregistry提 供的服务是针对特定的端口号的,不然在同一台机器上也是无法提供服务。

到此,有关RMI的原理的东西,就简单地介绍了,如果你还有什么疑问,请你留言,接下来我们来进行一个实例,复杂一些的实例(含带 UI),我在下一篇文章中介绍。

三、RMI简单实现

首先,我的环境:Windows XP,JDK1.6。所以我在这个实例中不再使用rmic编译stub和skeleton了。要实现的就是两个部分:客户端和 服务端。我们使用经典的Hello,world!例子。

文件清单如下:

接 口:MyRmiInterface.java 客户端:MyRmiClient.java 服务端:MyRmiImpl.java(实现类) MyRmiServer.java

你可以采用两台电脑,也可以一台,但是MyRmiInterface.class和MyRmiClient.class存在于客户端,MyRmiInterface.class, MyRmiImpl.class与MyRmiServer.class存在服务端。所有程序代码清单如下:

客户端:

  1. package net.csdn.blog.qb2049.exam;
  2. //MyRmiInterface.java
  3. import java.rmi.*;
  4. //这个接口必需继承Remote 接口
  5. public interface MyRmiInterface extends Remote
  6. {
  7. //声明方法 时,必需显式地抛出RemoteException异常
  8. public String sayHello() throws RemoteException;
  9. }
*************************************************************
  1. package net.csdn.blog.qb2049.exam;
  2. import java.rmi.*;
  3. import java.rmi.registry.*;
  4. //MyRmiClient.java
  5. public class MyRmiClient
  6. {
  7. public MyRmiClient(){}
  8. public static void main(String args[])
  9. {
  10. String host=(args.length<1)?null:args [0];
  11. try{
  12. //获得运行rmiregistry服务的主机上的注 册表
  13. Registry registry=LocateRegistry.getRegistry(host);
  14. //查询并获得远程对象的存根
  15. MyRmiInterface stub=(MyRmiInterface) registry.lookup("Hello");
  16. //像在使用本地对象方法那样,调用远程方法
  17. String response=stub.sayHello();
  18. System.out.println("response:"+response);
  19. }catch(Exception e)
  20. {
  21. System.out.println("Client exception :"+ e.toString());
  22. e.printStackTrace();
  23. }
  24. }
  25. }

服务端:(服务端也是需要存在接口)

  1. package net.csdn.blog.qb2049.exam;
  2. import java.rmi.*;
  3. import java.rmi.server.*;
  4. //这个类继承 UnicastRemoteObject非常重要
  5. //MyRmiImpl.java
  6. public class MyRmiImpl extends UnicastRemoteObject implements MyRmiInterface
  7. {
  8. //在实例化这个类时,就导出了远程对象,该构造方法必需
  9. public MyRmiImpl() throws RemoteException
  10. {
  11. super();
  12. }
  13. //实现接口中的方法,这个时间不需要显 式抛出RemoteException异常了
  14. public String sayHello()
  15. {
  16. return "Hello ,world";
  17. }
  18. }
*************************************************************
  1. package net.csdn.blog.qb2049.exam;
  2. import java.rmi.*;
  3. import java.rmi.registry.*;
  4. import java.rmi.server.*;
  5. //MyRmiServer.java
  6. public class MyRmiServer
  7. {
  8. public static void main(String args[])
  9. {
  10. try{
  11. //实例化远程对象,同时导出了该对象
  12. MyRmiImpl server=new MyRmiImpl ();
  13. //获得本地RMI注册表对象
  14. Registry registry=LocateRegistry.getRegistry();
  15. //在注册表中绑定远程对象
  16. registry.bind("Hello",server);
  17. //通告服务端已准备好了
  18. System.out.println("System already!");
  19. }catch(RemoteException e)
  20. {
  21. System.out.println("在建立远程连接的情况出现了异 常"+e.getMessage());
  22. System.out.println(e.toString());
  23. } catch(AlreadyBoundException e1){
  24. System.out.println("在向注册表 中绑定时出现了问题,名字已被绑定了!
  25. /n"+e1.getMessage());
  26. }
  27. }
  28. }
*************************************************************

实验环境目录如下图示:

博采众生

编译服务端

博采众生

编译客户端

博采众生

启动注册表环境,它没有任何输出

博采众生

启动服务端,提示ready!

博采众生

启动客户端端,得到Hello World!

博采众生

编译中可能出现的问题

启动中,可能由于codebase设置出现问题,总是抛出ClassNotFoundException异常,使你的服务器程序无法启动,建议你参考Java RMI实现代码动态下载来寻求帮助!

本文参考:
1、Java与CORBA 客户/服务器编程(第二版) 亢勇等译 电子工业出版社 2、Sun公司JDK文档 3、Understanding Java RMI Internals  Ahamed Aslam.K  Develper.com 4、Java2 核心技术卷II 王浩、姚建平等译 机械工业出版社 5、Java远程方法调用(Remote Method Invocation, RMI)  http://www.blogjava.net/yruc/

java反射机制详解 及 Method.invoke解释

JAVA反射机制JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为jav...
  • nemo2011
  • nemo2011
  • 2011年07月05日 14:43
  • 39513

Spring方法拦截器MethodInterceptor

实现MethodInterceptor 接口,在调用目标对象的方法时,就可以实现在调用方法之前、调用方法过程中、调用方法之后对其进行控制。MethodInterceptor 接口可以实现Metho...
  • zhangweikai966
  • zhangweikai966
  • 2011年04月19日 19:59
  • 39646

Java RMI详解

java远程方法调用
  • a19881029
  • a19881029
  • 2013年08月14日 11:10
  • 56274

数据处理----Java数据处理之RMI

也不知道叫个什么标题,就乱写了一个,这个专题的目的就是想梳理下在Hadoop等等分布式系统中可能用到的一些知识。很多复杂的技术,其实也是由很多基础组合而成的。有了牢固的基础,很多东西可以搭配在一起,自...
  • hugolyl
  • hugolyl
  • 2015年09月28日 10:37
  • 2057

struts.enable.DynamicMethodInvocation = true 动态方法调用

default.properties 在Struts 2的核心jar包-struts2-core中,有一个default.properties的默认配置文件。里面配置了一些全局的信息,比如:strut...
  • wfcaven
  • wfcaven
  • 2010年10月13日 09:57
  • 36441

【初学】RMI(Remote Method Invocation)初窥门径

【引言】 作为J2EE核心技术之一的RMI,它允许客服端调用一个远程服务器的组件,并返回调用结果(返回值或异常),可以完成分布式应用。整个调用过程由RMI实现,对使用者透明。 【Stub和Sk...
  • SMCwwh
  • SMCwwh
  • 2011年12月18日 00:31
  • 5010

初探RMI之几个常见异常排除

开发环境   eclipse java EE IDE,jdk1.6异常1 找不到类或存根java.rmi.ServerException: RemoteException occurred in se...
  • zlz3907
  • zlz3907
  • 2007年08月14日 13:11
  • 1138

JDK5新版RMI编程指南

                                                           JDK5新版RMI编程指南                            ...
  • shendl
  • shendl
  • 2007年08月02日 13:40
  • 7614

RMI原理及实现

编程   我们将首先编写远程对象,并将代码保存为名字为AddServer.Java的文件: import Java.rmi.*;  public interface AddSer...
  • u013456765
  • u013456765
  • 2014年05月22日 10:59
  • 1154

RMI原理及实现

简介   RMI是远程方法调用的简称,象其名称暗示的那样,它能够帮助我们查找并执行远程对象的方法。通俗地说,远程调用就象将一个class放在A机器上,然后在B机器中调用这个class的方法。 ...
  • chengzhezhijian
  • chengzhezhijian
  • 2014年11月25日 11:10
  • 1072
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:RMI(Remote Method Invocation)原理浅析
举报原因:
原因补充:

(最多只允许输入30个字)