向您介绍使用RMI实现Java的分布式计算。因为Java具备跨平台、代码可移植性、安全高效等普遍而强大的功能,于是在开发网络Java分布式应用的时候,能够用它自身的机制实现分布式计算。
概述java
随着电力企业信息化建设的不断深刻和发展,企业内部和企业与企业之间对信息、对数据的交换量大大增长,这些信息与数据愈来愈须要在不一样的计算机网络间传送和交流。同时,因为各单位、各部门之间的现存的计算机网络硬件设备与操做系统千差万别,应用水平也良莠不齐,所以,开发出跨平台、可移植、高效安全的网络分布式应用来服务于电力企业,就显得尤其重要。程序员
在当今的编程术语里,分布式计算已经成为很常见的词,它将企业的业务数据和程序分布在网络的不一样物理位置上,经过调动网络上多台计算机的处理能力,发挥远程调用数据的功能。编程
远程方法调用(Remote Method Invocation ,RMI),能够在不一样的Java虚拟机(JVM)之间实现对象与对象的通讯。JVM能够位于相同或不一样计算机上,在多个JVM中,一个JVM能够调用存储在其它JVM的对象的方法。安全
本文主要介绍RMI的特色,分析应用RMI进行企业分布式计算的原理,以及利用RMI实现基于Java的企业分布式应用的具体步骤。服务器
远程方法调用(RMI)的特色网络
一、TCP编程的缺点框架
因为Java编程语言设计之初就是面向对象和支持网络的,所以,基于对象的RMI机制已经内置在Java分布式计算平台中。编程语言
咱们常常会在网络开发中使用TCP/IP编程,这样,天然而然地就会涉及到Socket(套接字)编程。可是,使用Socket编程须要大量重复编码,在复杂分布式操做时显得很是麻烦,并且易于出错。所以,如何快速、高效、安全、可扩展地进行网络分布式计算,是开发者们一向追求和倡导的主题。直到RMI的出现,这种繁杂、低效的开发状况才有很大改观。分布式
二、RMI编程的特色ide
当咱们利用对象序列化在网络上分配对象时,RMI提供了非Java平台没法匹敌的独特而强大的分布式计算模型,RMI主要有如下特色:
客户机能够向本地方法同样调用远程服务器上的方法;
能够根据接口指定客户机/服务器编程合约;
能够从服务器对象缺省二进制类文件,自动生成调动/反调动代码;
将Java编程模型扩展到机器边界(和Java虚拟机(JVM)边界以外),不须要任何特殊语法;
还能够和一个远程方法调用中的数据同时传输行为(代码)。
尽管RMI不是惟一的企业级远程对象访问方案,但它倒是最容易实现的。
三、RMI与CORBA
做为分布式应用程序框架的规范,COBRA首当其冲,它是由对象管理组织(OMG)开发的。与CORBA不一样的是,CORBA可以利用不一样编程语言(例如C/C++、Basic等)开发实现分布式应用,而RMI是一种纯Java解决方案。在RMI中,程序的全部部分都由Java语言编写,这样,开发出来的程序彻底符合Java规范,便于实现跨平台访问、扩展和移植。按照笔者所在西北电力建设集团公司的状况看,服务器操做系统主要有Linux和Windows2000 Server,分别存在于公司和部门当中,它们是不一样的系统平台;同时,公司下属各个工程项目部又距离很远,近的几十千米,远则达到上千千米甚至位于国外,所以跨平台和远程访问这两大功能在开发企业应用系统时就必须考虑,而RMI偏偏可以用它的自身特色来知足编程须要。
RMI基本体系结构简介
RMI经过TCP/IP在内部使用Socket,象其名称暗示的那样,它可以帮助咱们查找并执行远程对象的方法。RMI的目的是让位于不一样JVM中的对象,在外观及行为上都像是本地的对象。
一般,咱们把调用这种远程对象的JVM,称为客户机;而把包括这种远程对象的JVM,称为服务器。
尽管对一个远程对象的引用和得到对本地对象的引用有所不一样,但咱们能够把远程对象像本地对象同样使用。应用程序并不知道一个对象是远程的仍是本地的。实际上,远程对象上被调用的方法与本地对象上调用的方法,具备相同的语法结构。
做为RMI的底层(会包含复杂的Socket操做),它会自动截获方法调用,找到远程对象,而后处理远程请求。笔者认为,RMI设计的重要之处,就在于不但在设计上实现了远程访问功能,并且实现了设计的透明性。
RMI的基本体系结构,归纳起来讲,由三个抽象层组成:
一、存根/框架层(Stubs/Skeletons Layer)
RMI为咱们引入了两种特殊类型的对象,称为存根(Stub)和框架(Skeleton),它们组成了RMI的第一层。
在远程通讯的时候,要利用TCP/IP协议,作不少底层数据的打包传输。运用Java分布式计算技术,咱们先要把数据或者对象转换成字节流(byte stream),便于网络传输,这个过程叫聚集(marshaling);当收到远程传来的字节流后,咱们要把流信息转换成对象或者数据,这个过程叫解读(unmarshaling),它与聚集恰好相反。
Stub和Skeleton层位于实际应用程序之下,创建在Proxy(代理)设计方案之上。Stub类的做用是远程服务器实现的代理的角色,Stub是客户方对象;Skeleton类用于帮助对象经过RMI连接与Stub通讯,它从链路中读取方法调用的参数,向远程服务实现对象进行调用,接受返回值,而后再把返回值写回到Stub。
二、远程引用层(Remote Reference Layer)
远程引用层定义和支持着RMI链接的调用语义(semantics)。
RMI进行远程访问要用到JRMP(Java Remote Method Protocol,即Java远程方法协议),这一层提供专用于JRMP的RemoteRef对象,它位于java.rmi.server包内,表明着远程对象的一个句柄。RemoteRef使用远程引用来执行远程对象的一个远程方法调用。
三、传输层(Transport Layer)
传输层在JVM之间创建基于流的网络链接,而且负责设置和管理这些链接。这时候,RMI使用一种线级(wire-level)协议进行基于TCP/IP的链接,该协议就是Java远程方法协议(JRMP,即Java Remote Method Protocol)。
在JDK版本1.2开始,JRMP再也不须要Skeleton,而是使用reflection来创建与远程服务的链接。为了生成Stub,咱们须用rmic。
当前的RMI实现中,传输层创建在TCP/IP基础上,设计用于在客户和服务器之间创建一条链接(即便联网有障碍)。
开发的基本步骤
咱们使用RMI编写Client/Server模式(客户/服务器)应用程序,包括6个基本步骤:
1) 定义远程接口
2) 实现远程接口
3) 准备远程调用的服务器对象
4) 生成残根Stub(客户代理)和框架Skeleton(服务器实体)
5) 用rmiregistry找到远程对象
6) 运行测试RMI分布式应用
开发企业信息发布系统实例
在开发RMI进行分布式访问以前,须要将各项功能模块化,即把实际应用抽象成符合Java规范的类和接口模型,使这些类和接口之间互相协做,能实现各自独立的功能,最后,能够把它们组合成统一的网络Java分布式计算系统。
如今,咱们就以开发公司信息发布系统为例,把主模块(主要的类文件)的名称暂定为InfoDistributeService(信息发布服务),为了保持应用开发的数据一致性和清晰度,接下来涉及的其它模块命名也将以这个模块命名为基准。
一、定义远程接口
Java RMI运行环境要求任何能够远程调用的方法必须放在远程接口中。
该远程接口用来扩展java.rmi.Remote接口,在Java API中,能够发现它没有任何方法,只是个标志性接口,这样,可让Java运行环境(JRE)认识每一个接口的特殊属性,以便可以远程访问。
所以,按照信息发布服务的命名(InfoDistributeService),首先须将InfoDistributeRemote定义为远程接口,同时仅放入一个供测试的方法 getRemoteInfo()来实现编码,将全部模块至于新建的enterprise.distribute包中,代码以下:
- // -----------InfoDistributeRemote.java-------------------
- package enterprise. distribute;
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- public interface InfoDistributeRemote extends Remote{
- public String getRemoteInfo() throws RemoteException;
- }
二、实现远程接口
这是一个实现远程对象的类。若是实现了远程接口,就可以覆盖(override)该对象中的全部方法,所以,远程对象的实现类将真正包含咱们但愿导出的方法的代码。
在远程信息发布系统中,咱们至少实现一个远程接口的对象,它就是远程可访问的对象。这里,InfoDistributeService类能够为咱们生成远程可访问对象的实例:
- // -----------InfoDistributeService.java------------------
- package enterprise. distribute;
- import java.rmi.RemoteException;
- import java.rmi.server.UnicastRemoteObject;
- public class InfoDistributeService
- extends UnicastRemoteObject implements InfoDistributeRemote{
- public InfoDistributeService() throws RemoteException{
- super();
- }
- // The return value of the method only for testing...
- public String getRemoteInfo(){
- return "Hello! I am a remote object.";
- }
- }
InfoDistributeService类实现远程接口InfoDistributeRemote,并继承java.rmi.server.UnicastRemoteObject。因为符
Java 2 Enterprise Edition(J2EE)远程方法调用(Remote Method Invocation,RMI)框架容许你建立透明的、分布式的服务和应用程序。基于RMI的应用程序由Java对象构成,这些对象相互调用,同时忽略对方的位置。换言之,一个Java对象可调用另外一个虚拟机上的某个Java对象的方法,整个过程和调用同一个虚拟机上的某个Java对象的方法无异。
驻留在不一样虚拟机上的对象为了相互得到引用,可使用RMI的查找服务,或者将对象引用做为方法调用的一个参数或者返回值来接收。参数和返回值借助Java的对象序列化机制由RMI来进行封送。
远程对象和接口
Java提供了一个彻底限定名称为java.rmi.Remote的接口。任何对象要想参与Java分布式计算和另外一个Java对象的远程会话,就必须直接或间接地实现该接口。尤为要注意的是,任何由java.rmi.Remote接口来标识的对象都暗示着它的方法可从其余任何虚拟机进行调用。实现了java.rmi.Remote接口的对象一般称为“远程对象”,必须采用如下方式来声明它的方法:
每一个支持远程调用的方法都必须在其throws子句中声明java.rmi.RemoteException。
对于一个可远程调用的方法,它的每一个非基本(nonprimitive)参数或者返回值都必须直接或间接地声明为实现了java.io.Serializable接口。
除了实现java.rmi.Remote接口和正确声明任何远程方法以外,Java分布式计算中远程对象必须提供一个无参数的构造函数,它能引起一个java.rmi.RemoteException异常。这就保证了对象可基于一种序列化状态来远程构造。
远程对象必须导出,以接收传入的远程方法调用。为此,你一般须要扩展java.rmi.server.UnicastRemoteObject或者java.rmi.activation.Activatable。经过对其中任何一个类进行扩展,远程对象就可在建立时自动导出。
RMI注册表
为了获取对远程对象的引用,RMI提供了名为注册表(registry)的一个远程对象,它将名称与远程对象关联起来。RMI服务器要向注册表注册每个远程对象,以便定位和检索对象。RMI客户端但愿调用远程对象上的一个方法时,首先必须根据远程对象的名称在注册表中定位远程对象。若是远程对象存在,注册表就返回对那个对象的一个引用。而后,要使用这个引用来发出对远程对象的方法调用。
RMI服务器
RMI采起一种客户机/服务器结构进行通讯。这意味着在RMI会话的某一端,必须有一个对象充当服务器,另外一端的对象则充当客户端。RMI服务器负责建立每一个远程对象的实例,并将每一个实例和RMI注册表中的一个名称绑定起来。RMI服务器能够自主,这要求它实现一个main方法,避免必须依赖其余类才能执行。
因为RMI服务器可从几乎任何主机下载和执行代码,因此每一个RMI服务器的main方法都须要安装一个安全管理器,防止它所加载的类表现失常。下例展现了如何实例化一个安全管理器,以及如何在RMI注册表中绑定一个对象实例:
- import java.rmi.RMISecurityManager;
- import java.rmi.Naming;
- public class SimpleRMIServer
- {
- public static void main(String[] args)
- {
- if (System.getSecurityManager() == null)
- {
- System.setSecurityManager(new RMISecurityManager());
- }
- try
- {
- TimeKeeperImplremoteObj = new TimeKeeperImpl();
- // Bind the remote object to the name "TimeKeeper"
- Naming.bind("//HostName/TimeKeeper", remoteObj);
- System.out.println("TimeKeeper successfully bound in registry");
- }
- catch (Exception e)
- {
- System.err.println("Error binding TimeKeeper: " + e.getMessage());
- }
- }
- }
小结
本文简单介绍了如何用RMI来隐藏远程交互问题,使程序员能将注意力集中在其余更重要的问题上,而没必要过多地考虑通讯基础结构。下一篇文章将进一步探索RMI,讲解RMI客户端如何定位远程对象,并调用其上的方法。
注:以上文章转自51CTO