原文地址:jGuru:
Remote Method Invocation (RMI)
如果您本文的翻译有任何建议,请留言。
这篇文章讲述Java2 平台中远程方法调用(RMI)技术的基础。
简介:使用RMI进行分布式计算
RMI(Remote Method Invocation,远程方法调用),在JDK1.1中被首次引入,它将网络编程提升到一个更高的层面。尽管相对来说,RMI比较易用,它仍然是一个引人注目的强大技术。它为普通的java开发者揭开了一个全新的角度——分布式对象计算的世界。
这篇文章将对这个通用的技术作一个深度的介绍。RMI技术被认为形成于JDK1.1,在Java 2 SDK中得到显著提升。在应用时,RMI在这两个版本中的不同点将会予以指明。
目标
对RMI的设计者而言,首要目标就是:允许程序员在开发分布式Java程序和非分布式程序时使用的语法和语义。为了做到这一点,他们的必须小心的将Java类和对象在单个JVM中的工作模型映射到分布式计算环境(多个JVM)中Java类和对象的新工作模型。
本节从分布式或远程Java对象的角度来介绍RMI框架,并分析它们与本地Java对象的行为上的不同。RMI框架定义了对象如何执行逻辑,异常何时和怎样发生,内如如何管理,和参数如何传递给远程方法,并获取返回值。
对比:分布式程序和非分布式程序
RMI架构力图使分布式Java对象与本地Java对象的使用方式相类似。虽然设计者成功的实现了这一点,但是仍有一些重要的不同点,如表所示:
即使你无法理解表中的所有不同点,也不必担心。但你分析RMI架构的时候,这些会变得清晰。你可以把这张表当作你学习RMI时的参考。
|
本地对象
|
远程对象
|
对象定义
|
本地对象由一个Java类定义
|
远程对象的外在行为由一个继承自Remote接口的接口定义。
|
对象实现
|
本地对象由它的Java类所实现
|
远程对象的行为由一个实现了Remote接口的Java类执行
|
对象创建
|
使用new运算符创建本地对象的一个新实例
|
在远程主机上使用new运算符创建一个远程对象的新实例。客户端不能直接创建远程对象(除非使用Java 2 的远程对象激活)
|
对象访问
|
本地对象可以使用对象引用直接访问
|
远程对象的访问,则需要使用一个指向代理对象(proxy stub)的引用,这个代理对象实现了远程接口。
|
引用
|
在单JVM中,一个对象引用直接指向JVM堆中一个对象。
|
“远程引用”是本地JVM堆中的代理对象的引用。这个代理对象包含一些信息,这些信息使得代理对象可以与远程对象进行交互。远程对象包含对应方法的实现。
|
终结化(Finalization)
|
如果一个对象实现了finalize()方法,在被垃圾回收器回收前,finalize()方法会被调用。
|
如果一个对象实现了unreferenced接口,在所有的远程引用断开时,接口中unreferenced方法会被调用。
|
垃圾回收
|
当对象的所有本地引用断开后,对垃圾回收器而言,该对象可回收。
|
分布式的垃圾回收器和本地垃圾回收器器共同工作。如果没有远程引用,并且所有远程对象的本地引用都断开了,那么对垃圾回收器而言,它可以通过正常的方式回收。
|
异常
|
要么是运行时异常,要么是编译时异常。Java编译器要求所有程序处理所有的编译时异常。
|
RMI迫使程序处理任何可能抛出的RemoteException 对象。这是为了保证分布式应用程序的健壮性。
|
Java RMI架构
RMI架构的设计目标是创建一个Java分布式对象模型,使之与Java编程语言和本地对象模型自然的集成。RMI架构成功了。针对分布式计算世界,它创建了一个系统。该系统继承了Java架构的安全性和健壮性。
接口:RMI的核心
RMI架构基于一个重要的原则:行为的定义和行为的实现是相互独立的概念。RMI允许行为定义的代码和行为的实现代码相分离和运行在各自独立的JVM中。
该原则很好的符合分布式系统的要求。在分布式系统中,客户端只关心服务的定义,服务端则关注服务的提供。
特别的,在RMI中,远程服务被定义在Java接口中;而远程服务的实现则在类中。因此,接口定义行为,类则定义行为的实现。理解RMI的关键是记住这一点。
下图说明了这种相互独立。
记住,Java接口并不包含运行的代码。RMI支持两个实现相同接口的类。第一个是行为的实现,它运行在服务端,第二个类作为远程服务的代理在客户端运行。如下图所示。
客户端程序通过代理对象调用服务方法,RMI将请求发送给远程JVM,然后将请求交给实现。实现提供的返回值被送回给代理,然后交给客户端程序。
RMI层级架构
在对RMI架构的上层理解后,来看看上层覆盖之下的底层实现吧。
RMI的实现本质上构建了三个抽象层。第一层是桩和框架层(the Stub and Skeleton layer),它正好位于开发者的视角之下。当客户端调用接口中定义的方法时,这一层拦截该方法调用并重定向到一个远程的RMI服务。
下一层是远程引用层。这一层知道如何拦截并管理远程服务对象的引用,这些引用由客户端创建。在JDK1.1中,这一层连接客户端和在服务器上运行的远程服务对象。该连接是一个一对一的连接。在Java 2 开发工具包中,这一层得到了强化,通过远程对象激活(Remote Object Activation),使其支持对处于休眠状态的远程服务对象的激活。
传输层是基于在一个网络中机器间的TCP/IP连接。它提供基本的连接以及一些防火墙穿透策略。
采用层级架构,可以在不影响系统其他部分的情况下,加强或替换某一层。例如,传输层可以使用UDP/IP协议替代,而无需影响上层。
桩和框架层(Stub and Skeleton Layer)
RMI的桩和框架层正好处于Java开发者的视线之下。在这一层,RMI使用了GOF的《设计模式》中的代理设计模式。在代理设计模式中,一个上下文中的对象是另一个上下文中对象的代理。代理对象在参与的对象间转寄方法调用。下图是代理模式的说明。
对于代理模式,在RMI中的运用,桩类(stub)代表代理,远程服务实现类代表真实对象(RealSubject)。
框架就是为使用RMI而产生的一个帮助类。框架知道如何通过RMI连接与桩类通信。框架会和桩进行会话。它读取来自连接的方法调用中的参数,调用远程服务实现对象,接受返回值,然后将返回值回写到给Stub对象。
在RMI的Java 2 SDK实现中,新的线协议废弃了框架类。RMI使用反射来产生对远程服务对象的连接。只有在JDK1.1或兼容JDK1.1的系统实现中,你才需要考虑框架类和对象。
远程引用层
远程引用层定义并支持对RMI连接的语义级调用。这一层提供一个远程引用对象。该对象代表了对远程服务实现对象的连接。
桩对象使用调用RemoteRef的invoke()方法转寄方法调用。远程引用对象对远程服务进行语义级的调用。
在RMI的JDK1.1实现版本中,只给客户端提供了一种连接到远程服务对象的方法:单向传播的、点对点的连接。在远程服务能被客户端使用之前,它必须在服务器上被实例化,并导出到RMI系统中(如果是首选服务,他必须命名并在RMI注册器上注册)。
RMI的Java2 SDK实现版本为客户-服务器连接增加了一个新的语义。在这个版本中,RMI支持可激活的远程对象。为了(调用)可激活的对象(的服务方法),客户端对代理产生一个方法调用,RMI根据远程对象是否处于休眠状态作出决定。如果它处于休眠状态,RMI将实例化这个对象,并从硬盘文件中恢复它的状态。一旦可激活对象加载到内存中,它就如同JDK1.1版本中的远程服务实现对象。
其它类型的连接在语义上是可能的。例如,采用多路广播,单个代理能同时将方法请求发给多个实现,并接受第一个回复(这勊改善相应时间,或许还可以改善可用性)。在将来,Sun会给RMI增加更多的语义调用。
传输层
传输层产生JVM间的连接。所有的连接都是基于流的网络,并采用TCP/IP。
即使是运行在同一个物理主机的两个JVM,它们之间仍然通过主机的TCP/IP协议栈进行通信(这是为什么如果你要运行本课的练习,在你的电脑上必须有一个可用的TCP/IP配置)。下图显示了在JVM间TCP/IP的自由使用。
正如你所知,在两台基于IP地址和端口号的主机间,TCP/IP提供持久的、基于流的连接。通常情况下,我们使用域名来代替IP地址。这意味着谈论flicka.magelang.com:3452和rosa.jguru.com:4432间的TCP/IP连接接。
在TCP/IP的顶端,RMI使用一个称为JRMP(Java Remote Method Protocol)的线级协议。JRMP是一个专有的,基于流的协议。目前,JRMP被部分地在两个版本中详细说明。RMI的第一个版本随着JDK1.1一起发布的,需要在服务器上使用框架类;第二个版本随着Java 2 SDK一起发布的。它在性能上得到优化,并无需框架类。(需要指出的是:一些可选的实现,例如BEA Weblogic 和 NinjaRMI并不使用JRMP,取而代之的是他们字节线级协议。“对象空间公司(ObjectSpace,一个软件公司)”的航海者(Voyager,该公司的产品)识别JPMP,可以在线级协议上与RMI进行交互。)
SUN和IBM联合致力于下一版的RMI,称为RMI-IIOP,它将会出现在Java 2
SDK 1.3中。关于RMI-IIOP,最有趣的事情是:在客户端和服务器通信时,它采用OMG(Obeject Management Group)的互联网内部对象请求代理协议(Internet
Inter-ORB Protocol)取代JRMP协议。OMG是一个有800多名会员的组织,它定义了一个厂商中立的,分布式对象架构,叫做公共对象请求代理体系架构(CORBA,Common Object Request Broker Architecture)。CORBA的对象请求代理客户端和服务器端采用IIOP进行通信。随着CORBA的对象传值扩展和从Java语言到接口定义语言(IDL, Interface Description Language)的映射的采用,为直接将RMI和CORBA集成的基础工作宣告完成。新版的RMI-IIOP实现支持大部分的RMI特性集,除了:
- Java.rmi.server.RMISocketFactory
- UnicastRemoteObject
- Unreferenced
- The DGC interfaces
<!--[if !supportLists]-->
RMI传输层的设计,是为了创建客户端和服务器端的连接,即使是在面临网络障碍的时候。
在一个客户端和服务器间,尽管传输层更喜欢使用多个TCP/IP连接,但是某些网络配置只允许使用单个TCP/IP连接(对于小程序(applet),一些浏览器只允许它和主机服务器间使用单个网络连接)。
在这种情况下,传输层使用单个TCP/IP连接,采用多路传输的方式虚拟多个连接。
远程对象命名
在展示RMI架构的时候,有一个问题被多次跳过,那就是:客户端如何找到RMI远程服务。现在,你会找到这个问题的答案。通过使用命名和目录服务,客户端才能找到远程服务。这看起来像个循环逻辑。一个客户端如何能通过使用一个服务来定位另一个服务。事实上,情况就是这样。一个命名和目录服务运行在一个广为人知的主机和端口上。
(广为人知,意味着在组织中的每个人都知道那是什么)。
RMI可以使用许多不同的目录服务,包括Java命名和目录接口(JNDI)。RMI本身包括一个叫做RMI注册器的简单服务,rmiregistry。RMI注册器运行在每一台机器上,默认使用1099端口,持有远程服务对象并接受查询。
使用RMI
现在,是时候构建一个可运行RMI系统了,从而获得一些亲身体验。在这一节,你将构建一个简单的远程计算服务,并通过客户端程序使用它。
一个可运行的RMI系统有以下几部分构成:
- 远程服务的接口定义
- 远程服务的实现
- 一台维持远程服务的服务器
-
一个RMI命名服务,使得客户端能够找到该远程服务。
-
一个类文件提供商(一个HTTP或FTP服务器)
- 一个需要远程服务的客户端程序
<!--[if !supportLists]-->
在下一节中,通过手把手的方式,你将构建一个简单的RMI系统。建议你在电脑上新建一个文件夹,当你读到对应的步骤时,建立那些文件。
为了简单起见,对客户端代码和服务器代码使用一个目录。从同一个目录中运行客户端和服务器端,你不需要启动一个HTTP或FTP服务器来提供类文件。(至于,具体如何使用HTTP和FTP服务器作为类文件提供商,这个问题将在“分布和安装RMI软件”一节中具体阐述。)
假设RMI系统已经有了设计,通过以下步骤,你就可以实现这个系统。
-
编写并编译接口的Java代码。
-
编写并编译实现类的Java代码
- 使用实现类来生成桩和框架类
- 编写提供远程服务的服务器程序
-
开发RMI客户端程序
-
安装和运行RMI系统
<!--[if !supportLists]-->
<!--[if !supportLists]-->1.
<!--[endif]-->接口
第一步是编写并编译服务的接口的Java代码。Calculator接口定义了所能提供的全部服务。
请注意,该接口继承自Remote ,每个方法签名都声明会抛出RemoteException对象。
拷贝该文件到你的文件夹中,并编译它:
javac
Calculator.java
实现
下一步,你要编写远程服务的实现。这是CalculatorImpl类:
再一次拷贝到这些代码到你的文件夹中,并编译它。
实现类使用UnicastRemoteObject 来连接RMI系统。在例子中,实现直接继承自UnicastRemoteObject。这并非必须。如果一个类不继承自UnicastRemoteObject,可能使用它的exportObject() 方法来连接RMI系统。
当一个类继承UnicastRemoteObject,它必须提供一个构造器,该构造器声明可能抛出RemoteException对象。当这个构造器调用super()方法时,它执行了UnicastRemoteObject中的某些代码,这些代码执行连接RMI和初始化远程对象的任务。
<!--[if !supportLists]-->2.
<!--[endif]-->桩和框架
下一步是,使用RMI编译器——rmic,来生成桩和框架文件。该编译器使用远程服务实现类文件来执行。
>rmic
CalculatorImpl
在你的目录下执行该命令。在你执行完rmic命令后,你将会发现Calculator_Stub.class文件,如果你在Java 2 SDK环境下,还有Calulator_Skel.class文件。
JDK1.1的RMI编译器rmic,可选项如下:
命令格式:rmic <options> <class names>
<options>包括:
-keep
不删除中间生成文件
-keepgenerated
(与”-keep”相同)
-g 生成调试信息
-depend 递归的重编译过期文件
-verbose 输出关于编译器正在做什么的信息
-classpath
<path> 指定寻找输入源文件和类文件
-d <directory> 指定生成文件存放路径
-J <runtime flag> 向java解释器传递参数
Java 2平台的rmic增加了三项新参数
-v1.1 创建JDK1.1桩协议版本的桩和框架
-vcompat (默认) 创建兼容JDK1.1和Java 2的桩协议版本的桩和框架
-v1.2 只创建Java 2桩协议版本的桩
<!--[if !supportLists]-->3.
<!--[endif]-->主机
远程RMI服务必须驻留在一个服务器进程中。类CalculatorServer是一个非常简单的服务器,它提供了最基本的要素。
<!--[if !supportLists]-->4.
<!--[endif]-->客户端
客户端源代码如下:
<!--[if !supportLists]-->5.
<!--[endif]-->运行RMI系统
现在,请准备运行该系统!需要启动三个控制台,一个给服务器,一个给客户端,一个给RMI注册器。
启动注册器,你必须切换到包含刚才你写的类文件的目录下。然后,输入如下命令:
rmiregistry
如果一切正常,注册器将会开始运行,你可以转换到下一个控制台。
在第二个控制台中,启动服务器,执行CalculatorService, 输入如下命令:
>java CalculatorServer
在最后一个控制台中,启动客户端程序
>java CalculatorClient
如果一切正常,你将会看到以下输出:
1
9
18
3
确是如此;你已经创建一个可运行的RMI系统。尽管你在同一台计算机上运行了三个控制台,在三个独立的JVM中,RMI仍然使用网络栈和TCP/IP来通信。这是一个全面的RMI系统。
分享到:
相关推荐
这个“RMI基础代码小程序”很可能是为了演示如何使用RMI进行基本的远程通信。 在Java RMI中,有以下几个核心概念: 1. **远程接口(Remote Interface)**:这是定义远程方法的接口,它扩展了java.rmi.Remote接口。...
### RMI基础教程:系统运行机理深度解析 #### RMI概述与应用场景 RMI(Remote Method Invocation)是Java平台提供的一种远程方法调用技术,它允许在不同JVM(Java虚拟机)上的对象互相通信,就如同本地调用一样...
总之,RMI是Java分布式计算的基础之一,虽然在现代开发中可能不如其他技术流行,但对于理解分布式系统原理和Java远程通信仍然具有重要的学习价值。通过深入学习和实践RMI,开发者可以更好地掌握分布式应用的设计和...
"RMI基础教程 - 深海有约 - BlogJava.files"和"rmi入门教程,简单明了(转).files"可能包含了相关的源代码或者图片资源,帮助读者更好地理解和实践RMI编程。 通过深入阅读这些资料,你可以了解如何设置RMI环境,...
1. RMI基础知识:解释RMI的概念,介绍其工作原理,包括客户端和服务端的角色。 2. 创建远程接口:讲解如何定义远程接口,它是远程方法调用的合同。 3. 实现远程对象:介绍如何实现远程接口,并创建远程对象实例,...
1. Java RMI基础:理解RMI的基本概念,如远程接口、远程对象、注册表以及传输协议。 2. RMI Lite简化点:分析源代码,找出与标准RMI相比,rmi-lite做了哪些简化和优化。 3. Ant构建工具:熟悉Ant的配置文件结构,...
1. RMI基础: - RMI的核心概念是远程对象,它是一个可以在不同JVM上执行的方法的代理。 - 远程接口定义了可以在远程对象上调用的方法,需要继承`java.rmi.Remote`接口。 - 实现远程接口的类是远程对象的具体实现...
### RMI基础知识与实践 #### 一、Java RMI简介 Java Remote Method Invocation (RMI) 是Java平台提供的一种分布式计算技术,允许开发者在不同Java虚拟机(JVM)之间调用远程对象的方法,从而实现分布式应用程序的...
5. **启动RMID服务**:确保服务器上运行着RMID守护进程,它是RMI基础设施的一部分。 6. **客户端调用**:在客户端,通过`Naming.lookup()`或`InitialContext`查找服务器上的`BankService`,然后就可以像调用本地...
1. **RMI基础**: - **远程接口**:RMI的核心是远程接口,它定义了可以在远程对象上调用的方法。这些接口使用`java.rmi.Remote`作为父接口,并且声明抛出`java.rmi.RemoteException`。 - **远程对象**:实现远程...
#### 二、RMI基础知识 在深入理解给定示例之前,我们首先简要介绍一些RMI的基础概念: 1. **接口**: 在RMI中,接口必须继承自`java.rmi.Remote`接口,并声明所有可能抛出`RemoteException`的方法。 2. **服务端实现*...
企业级JavaBeans(EJB)是在RMI基础上发展起来的,提供了更高级的服务,如事务管理、安全性、生命周期管理和资源池等。 **RMI的优化** 在实际应用中,可能需要优化RMI性能,如减少远程调用次数,使用批量调用,或者...
### RMI基础 RMI 是Java平台上的一个标准API,用于创建分布式应用程序。它允许Java对象在不同的JVM之间进行交互,仿佛它们在同一台机器上运行。RMI的核心概念包括:远程接口、远程对象和远程方法调用。 ### 动态...
一、RMI基础概念 1. 远程接口(Remote Interface):定义了远程对象需要暴露的方法,这些方法将在远程服务器上执行。 2. 远程实现(Remote Implementation):实现了远程接口的类,是实际执行远程方法的对象。 3. ...
一、Spring整合RMI基础 1.1 RMI原理:RMI的核心在于Java的`java.rmi`包,它定义了接口和类来创建远程对象并进行调用。远程对象需继承`java.rmi.Remote`接口,并声明所有远程方法抛出`java.rmi.RemoteException`。 ...
### Java RMI基础知识与示例解析 #### 一、RMI简介 远程方法调用(Remote Method Invocation,简称RMI)是Java平台提供的一种用于实现分布式应用的技术。它允许对象在不同的JVM之间通过网络相互调用对方的方法,就...
- **RMIHelloWorld.rar**:这是一个RMI基础示例的压缩包,通常包含了一个简单的RMI客户端和服务器端的源代码,帮助初学者理解RMI的基本工作原理和交互流程。通过解压并运行这个例子,你可以看到RMI如何实现远程方法...
1. **RMI基础概念**: - **远程对象**:在RMI中,远程对象是可以在网络上访问的对象,它们可以跨JVM边界调用方法。 - **接口定义**:定义远程对象需通过接口,该接口必须声明为`remote`,并包含`throws java.rmi....
1. **RMI基础知识**:RMI是Java提供的一种标准API,用于在分布式环境中调用远程对象的方法。它包括三个主要步骤:定义远程接口、实现远程接口和服务端绑定远程对象到RMI注册表、客户端通过注册表查找并调用远程对象...