RMI的局限性之二是RMI是Java语言的远程调用,两端的程序语言必须是Java实现,对于不一样语言间的通信能够考虑用Web Service或者公用对象请求代理体系(CORBA)来实现。
如今的问题在于代理之间是如何进行通讯的?一般有三种方法:程序员
一、CORBA:经过对象请求代理架构,支持任何编程语言编写的对象之间的方法调用。数据库
二、SOAP编程
三、RMI:JAVA的远程方法调用技术,支持java的分布式对象之间的方法调用。安全
其中CORBA与SOAP都是彻底独立于言语的,可使用C、C++、JAVA来编写,而RMI只适用于JAVA。服务器
相关概述
RMI是Java的一组拥护开发
分布式应用程序的
API。RMI使用Java语言
接口定义了远程对象,它集合了Java序列化和Java远程方法协议(Java Remote Method Protocol)。简单地说,这样使原先的程序在同一操做系统的方法调用,变成了不一样操做系统之间程序的方法调用,因为J2EE是分布式程序平台,它以RMI机制实现程序组件在不一样操做系统之间的通讯。好比,一个EJB能够经过RMI调用Web上另外一台机器上的EJB远程方法。
RMI(Remote Method Invocation,远程方法调用)是用Java在JDK1.1中实现的,它大大加强了Java开发
分布式应用的能力。Java做为一种风靡一时的网络开发语言,其巨大的威力就体如今它强大的开发分布式网络应用的能力上,而RMI就是开发百分之百纯Java的网络分布式应用系统的核心解决方案之一。其实它能够被看做是RPC的Java版本。可是传统RPC并不能很好地应用于
分布式对象系统。而Java RMI 则支持存储于不一样
地址空间的程序级对象之间彼此进行通讯,实现远程对象之间的无缝远程调用。
RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通讯。JRMP是专为Java的远程对象制定的协议。所以,Java RMI具备Java的“Write Once,Run Anywhere”的优势,是
分布式应用系统的百分之百纯Java解决方案。用Java RMI开发的应用系统能够部署在任何支持JRE(Java Run Environment Java,运行环境)的平台上。但因为JRMP是专为Java对象制定的,所以,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通讯。
Java Remote Method Invocation ( RMI -- Java远程方法调用)容许您使用Java编写
分布式对象。本文将介绍RMI的优势以及如何将其链接到现有的和原有的系统中,以及与用Java 编写的组件的链接。
RMI为采用Java对象的
分布式计算提供了简单而直接的途径。这些对象能够是新的Java对象,也能够是围绕现有API的简单的Java包装程序。Java体现了“编写一次就能在任何地方运行的模式。而RMI可将Java模式进行扩展,使之可在任何地方运行”。
由于RMI是以Java为核心的,因此,它将Java的安全性和可移植性等强大功能带给了分布式计算。您可将代理和业务逻辑等属性移动到网络中最合适的地方。若是您要扩展Java在系统中的使用,RMI将使您充分利用其强大功能。
RMI可利用标准Java本机方法
接口JNI[1]
与现有的和原有的系统相链接。RMI还可利用标准JDBC包与现有的关系数据库链接。RMI/JNI和RMI/JDBC相结合,可帮助您利用RMI与目前使用非Java语言的现有
服务器进行通讯,并且在您须要时可扩展Java在这些服务器上的使用。RMI可帮助您在扩展使用时充分利用Java的强大功能。
RMI系统运行机理
RMI应用程序一般包括两个独立的程序:
服务器程序和客户机程序。典型的服务器应用程序将建立多个远程对象,使这些远程对象可以被引用,而后等待客户机调用这些远程对象的方法。而典型的客户机程序则从服务器中获得一个或多个远程对象的引用,而后调用远程对象的方法。RMI为服务器和客户机进行通讯和信息传递提供了一种机制。
在与远程对象的通讯过程当中,RMI使用标准机制:stub和skeleton。远程对象的stub担当远程对象的客户本地表明或代理人角色。调用程序将调用本地stub的方法,而本地stub将负责执行对远程对象的方法调用。在RMI中,远程对象的stub与该远程对象所实现的远程
接口集相同。调用stub的方法时将执行下列操做:
(1) 初始化与包含远程对象的远程
虚拟机的链接;
(2) 对远程虚拟机的参数进行编组(写入并传输);
(3) 等待方法调用结果;
(4) 解编(读取)返回值或返回的异常;
(5) 将值返回给调用程序。为了向调用程序展现比较简单的调用机制,stub将参数的序列化和网络级通讯等细节隐藏了起来。在远程虚拟机中,每一个远程对象均可以有相应的skeleton(在JDK1.2环境中无需使用skeleton)。Skeleton负责将调用分配给实际的远程对象实现。它在接收方法调用时执行下列操做:(1) 解编(读取)远程方法的参数;(2) 调用实际远程对象实现上的方法;(3) 将结果(返回值或异常)编组(写入并传输)给调用程序。stub和skeleton由rmic
编译器生成。
利用RMI编写
分布式对象应用程序须要完成如下工做:(1) 定位远程对象。应用程序可以使用两种机制中的一种获得对远程对象的引用。它既可用RMI的简单命名工具rmiregistry来注册它的远程对象,也能够将远程对象引用做为常规操做的一部分来进行传递和返回。(2)与远程对象通讯。远程对象间通讯的细节由RMI处理,对于程序员来讲,远程通讯看起来就像标准的Java方法调用。(3)给做为参数或返回值传递的对象加载类
字节码。由于RMI容许调用程序将纯Java对象传给远程对象,因此,RMI将提供必要的机制,既能够加载对象的代码又能够传输对象的数据。在RMI
分布式应用程序运行时,
服务器调用注册服务程序以使名字与远程对象相关联。客户机在服务器上的注册服务程序中用远程对象的名字查找该远程对象,而后调用它的方法。
系统组成
一个正常工做的RMI系统由下面几个部分组成:
·桩(Stub)和框架(Skeleton)文件
·一个RMI命名服务,它容许客户端去发现这个远程服务
·一个须要这个远程服务的客户端程序
技术原理
RMI系统结构,在客户端和
服务器端都有几层结构。
--------- ----------
| 客户 | |服务器|
---------- ----------
| |
------------- ----------
-------------- -----------
| |
------------------------------------
| 远 程 引 用 层 |
------------------------------------
| |
------------------------------------
| 传 输 层 |
------------------------------------
方法调用从客户对象经占位程序(Stub)、远程引用层(Remote Reference Layer)和
传输层(Transport Layer)向下,传递给主机,而后再次经传 输层,向上穿过远程调用层和骨干网(Skeleton),到达
服务器对象。 占位程序扮演着远程服务器对象的代理的角色,使该对象可被客户激活。 远程引用层处理语义、管理单一或多重对象的通讯,决定调用是应发往一个服务器仍是多个。传输层管理实际的链接,而且追追踪能够接受方法调用的远程对象。服务器端的骨干网完成对服务器对象实际的方法调用,并获取返回值。返回值向下经远程引用层、服务器端的
传输层传递回客户端,再向上经传输层和远程调用层返回。最后,占位程序得到返回值。
要完成以上步骤须要有如下几个步骤:
三、生成占位程序和骨干网(服务器端程序)
四、编写服务器程序
五、编写客户程序
六、注册远程对象
七、启动远程对象
具体实现以下:
一、生成一个远程接口
package c15.ptime;
importjava.rmi.*;
public interface PerfectTimeI extends Remote {
long getPerfectTime() throws RemoteException;
}
package c15.ptime;
import java.rmi.*;
import java. net.*;
public class PerfectTime extends UnicastRemoteObject implements PerfectTimeI {
public long getPerfectTime() throws RemoteException {
return System.currentTimeMillis();
}
public PerfectTime() throws RemoteException {
super();
}
public static void main(String[] args) {
try {
PerfectTime pt = new PerfectTime();
LocateRegistry.createRegistry(2005);
Naming.rebind( "//zhouty:2005/PerfectTime" , pt);
System.out.println("Ready to do time");
} catch(Exception e) {
e.printStackTrace();
}
}
}
四、编译远程对象(服务器端程序)
javac -classpath . -d . PerfectTime.java
五、生成根和干(占位程序和骨干程序)
rmic -classpath . -d . c15.ptime.PerfectTime
注:jdk1.2之后的都不须要skeleton,因此若是你用的jdk为5.0版本的,
不要奇怪为何只产生了stub没有skeleton。
六、注册远程对象
start rmiregistry 2005
注:绑定服务的默认端口为1099,若是使用了这个端口,则能够直接使用 start rmiregistry而不须要跟端口
若是这种注册远程对象的方法不起做用.
还有一种方法就是在绑定服务以前使用LocateRegistry.createRegistry(1099) 来注册远程对象.
fectTime
八、编写客户端程序
package c15.ptime;
import java.rmi.*;
public class DisplayPerfectTime {
public static void main(String[] args) {
System.setSecurityManager( new RMISecurityManager());
try {
PerfectTimeI t = (PerfectTimeI)Naming.lookup( "192.168.0.171:2005/PerfectTime");
for(int i = 0 ; i < 10; i++)
System.out.println("Perfect time =" +
t.getPerfectTime());
} catch(Exception e) {
e.printStackTrace();
}
}
}
九、编译客端程序
javac -classpath . -d . DisplayPerfectTime.java
十、修改JVM的配置文件 (客户机和
服务器的都须要通过修改)
%JRE_HOME%\policytool.exe
十一、启动客户程序
java -classpath . c15.ptime.DisplayPerfectTime
十二、返回结果
Perfect time =967274884390
Perfect time =967274884450
Perfect time =967274884450
Perfect time =967274884450
Perfect time =967274884500
Perfect time =967274884500
Perfect time =967274884560
Perfect time =967274884610
Perfect time =967274884610
Perfect time =967274884610
RMI(远程方法调用)的优势
从最基本的角度看,RMI是Java的
远程过程调用(RPC)机制。与传统的RPC系统相比,RMI具备若干优势,由于它是Java
面向对象方法的一部分。传统的RPC系统采用中性语言,因此是最普通的系统--它们不能提供全部可能的目标平台所具备的功能。
RMI以Java为核心,可与采用本机方法与现有系统相链接。这就是说,RMI可采用天然、直接和功能全面的方式为您提供
分布式计算技术,而这种技术可帮助您以不断递增和无缝的方式为整个系统添加Java功能。
RMI的主要优势以下:
面向对象:RMI可将完整的对象做为参数和返回值进行传递,而不只仅是预约义的数据类型。也就是说,您能够将相似Java
哈希表这样的复杂类型做为一个参数进行传递。而在目前的RPC系统中,您只能依靠客户机将此类对象分解成基本数据类型,而后传递这些数据类型,最后在
服务器端从新建立哈希表。RMI则不需额外的客户程序代码(将对象分解成基本数据类型),直接跨网传递对象。
可移动属性:RMI可将属性(类实现程序)从客户机移动到服务器,或者从服务器移到客户机。例如,您能够定义一个检查雇员开支报告的
接口,以便察看雇员是否遵照了公司目前实行的政策。在开支报告建立后,客户机就会从服务器端得到实现该接口的对象。若是政策发生变化,服务器端就会开始返回使用了新政策的该接口的另外一个实现程序。您没必要在用户系统上安装任何新的软件就能在客户端检查限制条件--从而向用户提供烁快的反馈,并下降
服务器的工做量。这样就能具有最大的灵活性,由于政策改变时只须要您编写一个新的Java类,并将其在服务器
主机上安装一次便可。
设计方式:对象传递功能使您能够在分布式计算中充分利用
面向对象技术的强大功能,如二层和
三层结构系统。若是您可以传递属性,那么您就能够在您的解决方案中使用
面向对象的设计方式。全部面向对象的设计方式无不依靠不一样的属性来发挥功能,若是不能传递完整的对象--包括实现和类型--就会失去设计方式上所提供的优势。
安 全:RMI使用Java内置的安全机制保证下载执行程序时用户系统的安全。RMI使用专门为保护系统免遭恶意
小应用程序侵害而设计的安全
管理程序,可保护您的系统和网络免遭潜在的恶意下载程序的破坏。在状况严重时,
服务器可拒绝下载任何执行程序。
便于编写和使用:RMI使得Java
远程服务程序和访问这些服务程序的Java客户程序的编写工做变得轻松、简单。远程
接口实际上就是Java接口。服务程序大约用三行指令宣布自己是服务程序,其它方面则与任何其它Java对象相似。这种简单方法便于快速编写完整的
分布式对象系统的服务程序,并快速地制作软件的原型和早期版本,以便于进行测试和评估。由于RMI程序编写简单,因此维护也简单。
可链接现有/原有的系统:RMI可经过Java的本机方法接口JNI与现有系统进行进行交互。利用RMI和JNI,您就能用Java语言编写客户端程序,还能使用现有的
服务器端程序。在使用RMI/JNI与现有服务器链接时,您能够有选择地用Java从新编写服务程序的任何部分,并使新的程序充分发挥Java的功能。相似地,RMI可利用JDBC、在不修改使用数据库的现有非Java
源代码的前提下与现有关系数据库进行交互。
编写一次,处处运行:RMI是Java“编写一次,处处运行 ”方法的一部分。任何基于RMI的系统都可100%地移植到任何Java
虚拟机上,RMI/JDBC系统也不例外。若是使用RMI/JNI与现有系统进行交互工做,则采用JNI编写的代码可与任何Java虚拟机进行编译、运行。
分布式垃圾收集:RMI采用其分布式垃圾收集功能收集再也不被网络中任何客户程序所引用的远程服务对象。与Java虚拟机内部的垃圾收集相似,分布式垃圾收集功能容许用户根据本身的须要定义
服务器对象,而且明确这些对象在再也不被客户机引用时会被删除。
并行计算:RMI采用多线程处理方法,可以使您的服务器利用这些Java线程更好地
并行处理客户端的请求。Java分布式计算解决方案:RMI从JDK 1.1开始就是
Java平台的核心部分,所以,它存在于任何一台1.1 Java虚拟机中。全部RMI系统均采用相同的公开协议,因此,全部Java 系统都可直接相互对话,而没必要事先对协议进行转换。
RMI与CORBA的关系
RMI 和 CORBA 常被视为相互竞争的技术,由于二者都提供对远程
分布式对象的透明访问。但这两种技术其实是相互补充的,一者的长处正好能够弥补另外一者的短处。RMI 和 CORBA 的结合产生了
RMI-IIOP,RMI-IIOP 是企业
服务器端 Java 开发的基础。
1997 年,IBM 和 Sun Microsystems启动了一项旨在促进 Java 做为企业开发技术的发展的合做计划。两家公司特别着力于如何将 Java 用做服务器端语言,生成能够结合进现有体系结构的企业级代码。所须要的就是一种远程传输技术,它兼有 Java 的 RMI(Remote Method Invocation,远程方法调用)较少的资源占用量和更成熟的 CORBA(Common Object Request Broker Architecture,
公共对象请求代理体系结构)技术的
健壮性。出于这一须要,RMI-IIOP问世了,它帮助将 Java 语言推向了目前
服务器端企业开发的主流语言的领先地位。
RMI示例
Java远程方法调用(RMI)提供了Java程序语言的远程通信功能,这种特性使客户机上运行的程序能够调用远程服务器上的对象,使Java编程人员可以在网络环境中分布操做。
建立一个简单的Java分布式远程方法调用程序能够按如下几个步骤操做,
在 Java 中,远程对象是实现远程接口的类的实例, 远程接口声明每一个要远程调用的方法。在须要建立一个远程对象的时候,咱们经过传递一个接口来隐藏基层的实施细节,客户经过接口句柄发送消息便可。
远程接口具备以下特色:
1) 远程接口必须为public属性。若是不这样,除非客户端与远程接口在同一个包内,不然 当试图装入实现该远程接口的远程对象时,调用会获得错误结果。
2) 远程接口必须扩展接口java.rmi.Remote。
3) 除与应用程序自己特定的例外以外,远程
接口中的每一个方法都必须在本身的throws从句中 声明java.rmi.RemoteException。(或RemoteException 的父类)。
4) 做为参数或返回值传递的一个远程对象(无论是直接,仍是本地对象中嵌入)必须声明为远 程接口,而不该声明为实施类。
下面是远程接口的接口RmiSample的定义
Java代码
import java.rmi.*; public interface RmiSample extends Remote { public int sum(int a,int b) throws RemoteException; }
远程对象实现类必须扩展远程对象java.rmi.UnicastRemoteObject类,并实现所定义的远程接口。远程对象的实现类中包含实现每一个远程接口所指定的远程方法的代码。这个类也能够含有附加的方法,但客户只能使用远程接口中的方法。由于客户是指向接口的一个句柄,而不是它的哪一个类。必须为远程对象定义
构造函数,即便只准备定义一个
默认构造函数,用它调用基础类构造函数。由于基础类构造函数可能会抛出java.rmi.RemoteException,因此即便别无它用必须抛出java.rmi.RemoteException例外。
如下是远程对象实现类的声明:
Java代码
import java.rmi.*;
public class RmiSampleImpl extends UnicastRemoteObject implements RmiSample {
RmiSampleImpl() throws RemoteException {
super();
}
public int sum(int a,int b) throws RemoteException {
return a + b;
}
}
包含 main 方法的类能够是实现类自身,也能够彻底是另外一个类。下面经过RmiSampleServer 来建立一个远程对象的实例,并经过
java.rmi.registry.LocateRegistry类的createRegistry 方法从指定
端口号启动注册服务程序,也能够经过执行 rmiregistry 命令启动注册服务程序,注册服务程序的缺省运行端口为 1099。必须将远程对象名字绑定到对远程对象的引用上:Naming.rebind("//localhost:8808/SAMPLE-SERVER" , Server);
Java代码
import java.rmi.*;
import java.rmi.registry.*;
public class RmiSampleServer{
public static void main(String args[]) {
try {
LocateRegistry.createRegistry(8808) ;
SampleServerImpl Server = new SampleServerImpl();
// 将该对象实例与名称“SAMPLE-SERVER”捆绑
Naming.rebind("//localhost:8808/SAMPLE-SERVER" , Server);
} catch (MalformedURLException me) {
System.out.println("Malformed URL: " + me.toString());
} catch (RemoteException re) {
System.out.println("Remote exception: " + re.toString());
}
}
}
客户机类的主要功能有两个,一是经过Naming.lookup方法来构造注册服务程序stub 程序实例,二是调用
服务器远程对象上的远程方法。
如下是服务器类的声明:
Java代码
import java.rmi.*;
public class RmiSampleClient { public static void main(String[] args) {
try {
String url = "//localhost:8808/SAMPLE-SERVER";
RmiSample RmiObject = (RmiSample)Naming.lookup(url);
System.out.println(" 1 + 2 = " + RmiObject.sum(1,2) );
} catch (RemoteException exc) {
System.out.println("Error in lookup: " + exc.toString());
} catch (MalformedURLException exc) {
System.out.println("Malformed URL: " + exc.toString());
} catch (java.rmi.NotBoundException exc) {
System.out.println("NotBound: " + exc.toString());
}
}
}
5、编译代码:
要编译 Java 源文件,请运行javac 命令:
Java代码
javac RmiSample.java RmiSampleImpl.java RmiSampleServer.java RmiSampleClient.java
javac RmiSample.java RmiSampleImpl.java RmiSampleServer.java RmiSampleClient.java
6、为远程对象实现建立根和干:
要建立存根程序和骨架文件,应以包含远程对象实现的已编译类包全名运行 rmic
编译器。
存根(Stub)是远程对象在客户端的代理,它将RMI调用传递给
服务器端的骨架(Skeleton),后者负责将该调用传递给实际的远程方法输入以下:
Java代码
D:\RMI>rmic -d D:\RMI RmiSampleImpl
D:\RMI>rmic -d D:\RMI RmiSampleImpl 执行这个命令, 若rmic成功运行,RMI目录下就会多出两个新类: RmiSampleImpl_Stub.class RmiSampleImpl_Skel.class 它们分别对应的是存根(stub)和骨架(skeleton).
7、运行代码:
运行服务端程序:在Windows下,输入下列命令,在后台启动RmiSampleServer程序:
Java代码
D:\RMI>java RmiSampleServer
D:\RMI>java RmiSampleServer
运行客户端程序:
Java代码
D:\RMI>java RmiSampleClient
D:\RMI>java RmiSampleClient
客户端输出: 1 + 2 = 3
RMI(Retailer Managed Inventory)零售商管理库存 RMI(Retailer Managed Inventory,RMI),零售商管理库存。是一种传统的库存管理方法,相对于VMI(Vendor Managed Inventory,
供应商管理库存)而言,由零售商根据本身的销售须要来控制管理库存。