使用RMI和CORBA进行分布式java程序设计.docx
《使用RMI和CORBA进行分布式java程序设计.docx》由会员分享,可在线阅读,更多相关《使用RMI和CORBA进行分布式java程序设计.docx(13页珍藏版)》请在冰豆网上搜索。
使用RMI和CORBA进行分布式java程序设计
使用RMI和CORBA进行分布式java程序设计
(bylizhi)分布式程序设计讨论>>>
英文原文:
RMI和corba是两种最重要和使用最广泛的分布式对象系统。
每种都有它的长处和短处。
这两种系统都在从电子商务到卫生保健等不同的行业成功的使用。
在项目中使用这两种分布机制中的任何一种都是一项很困难的任务。
本文介绍了RMI和corba的机理和最主要的是显示了如何开发一个有用的程序(一个从远程站点下载文件的程序)。
于是有以下内容:
▪一个分布式系统的简介
▪一个RMI和corba的简介
▪让你体验开发一个RMI和corba程序的滋味。
▪说明如何使用RMI和corba从远程机器交换文件
▪提供一个RMI和Corba的比较。
客户/服务端模式
客户/服务模式是一个分布式计算应用。
它通过使用一个应用程序(客户)和另一个程序(服务端)交换数据。
在这样的一个例子里面客户端和服务端一般使用同样的语言来编写,使用相同的协议来相互通信。
在客户/服务模式应用到各种各样的地方的过程中,使用低层次的socket来开发是很典型的。
使用socket来开发客户/服务端模式意味着我们必须自己设计一种协议,该协议包含客户端和服务端都统一的命令集,使得客户端和服务端能够通过这个协议来通信。
例如:
HTTP协议提供了一个get的方法。
所有的web服务器软件都集成了该功能,而所有的浏览器软件都能够使用该功能来获得资料。
分布式对象模式
分布式对象系统是一个对象集合,通过定义很完善的统一的接口来分隔开的要求服务(客户端)和功能服务(服务端)。
换句话说客户端和公共服务的提供分隔开,这些服务包括数据表现和执行的代码。
这是一个分辨分布式对象模式和客户/服务模式的主要不同。
在分布式对象模式里,客户端发送一个消息到一个对象,由这个对象解释这个消息然后决定应该由什么服务来完成。
这个服务,方法或选择的完成可能是被一个对象或是被一个broker。
RMI和corba就是这种模式的例子。
RMI
RMI是一个分布式对象模式。
它使得使用java开发分布式程序更加容易。
由于不需要设计协议(这基本是一个错误的任务)使得使用RMI开发分布式程序比使用socket更加容易。
在RMI里面设计者就象在调用一个本地的类的方法一样,而实际上是在调用的时候相应的参数被发送到远端的对象和然后被解释。
最后结果返回给调用者。
一个RMI应用的流程
使用RMI开发一个分布式应用包括如下几个步骤
1)定义一个远端的接口
2)实现这个远端的接口
3)开发一个服务端
4)开发一个客户端
5)生成Stubs和Skeletons,运行RMI注册器,服务端和客户端
我们现在通过开发一个文件交换程序来解释这些步骤
例子:
文件交换程序
这个应用允许客户端从服务端交换(或下载)所有类型的文件。
第一步是定义一个远程的接口,这个接口指定了的签名方法将被服务端提供和被客户端调用。
定义一个远程接口
这个程序的远程接口在代码例子1中列出,接口FileInterface提供一个downloadFile这个带一个字符窜(文件名)变量的的方法,然后返回以一个字符窜序列的形式的相应文件数据。
代码例子1:
FileInterface.java
importjava.rmi.Remote;
importjava.rmi.RemoteException;
publicinterfaceFileInterfaceextendsRemote{
publicbyte[]downloadFile(StringfileName)throws
RemoteException;
}
注意接口FileInterface的如下特征:
▪它必须定义成public,这是为了让客户端能够通过调用远程接口来间接调用远程的对象。
▪必须使用从Remote接口扩展过来,这是创建一个远程的对象的需要。
▪每个接口方法中必须抛出java.rmi.RemoteException错误。
实现远程的接口
下一步是实现远程的接口FileInterface。
实现的例子在代码例子2中列出。
类FileImpl从UnicastRemoteObject扩展来。
这显示出FileImpl类是用来创建一个单独的,不能复制的,远程的对象,
这个对象使用RMI的默认的基于TCP的通信方式。
代码例子2:
FileImpl.java
importjava.io.*;
importjava.rmi.*;
importjava.rmi.server.UnicastRemoteObject;
publicclassFileImplextendsUnicastRemoteObject
implementsFileInterface{
privateStringname;
publicFileImpl(Strings)throwsRemoteException{
super();
name=s;
}
publicbyte[]downloadFile(StringfileName){
try{
Filefile=newFile(fileName);
bytebuffer[]=newbyte[(int)file.length()];
BufferedInputStreaminput=new
BufferedInputStream(newFileInputStream(fileName));
input.read(buffer,0,buffer.length);
input.close();
return(buffer);
}catch(Exceptione){
System.out.println("FileImpl:
"+e.getMessage());
e.printStackTrace();
return(null);
}
}
}
编写服务端
第3步是实现一个服务端。
有3件事服务端需要去做:
1)创建一个RMISecurityManager实例,然后安装它。
2)创建一个远程对象的实例(这个例子中是FileImpl)
3)使用RMI注册工具来注册这个对象。
代码例子3中显示了如何操作的。
代码例子3:
FileServer.java
importjava.io.*;
importjava.rmi.*;
publicclassFileServer{
publicstaticvoidmain(Stringargv[]){
if(System.getSecurityManager()==null){
System.setSecurityManager(newRMISecurityManager());
}
try{
FileInterfacefi=newFileImpl("FileServer");
Naming.rebind("//127.0.0.1/FileServer",fi);
}catch(Exceptione){
System.out.println("FileServer:
"+e.getMessage());
e.printStackTrace();
}
}
}
声明Naming.rebind("//127.0.0.1/FileServer",fi)中假定了RMI注册工具(RMIregistry)使用1099端口并在运行中。
如果你在其他的端口运行了RMI注册工具,你必须在这个声明中定义。
例如如果RMI注册工具在4500端口运行。
你的声明要变成
Naming.rebind("//127.0.0.1:
4500/FileServer",fi)
另外我们已经同时假定了我们的服务端和RMI注册工具是运行在同一台机器上的。
如果不是的话你要修改rebind方法中的地址。
编写客户端下一步是编写一个客户端,客户端可以远程调用远程接口(FileInterface)中说明的任何一个方法。
无论如何实现,客户端必须先从RMI注册工具获得一个远程对象的引用。
当引用获得后方法downloadFile被调用。
客户端的例子在代码例子4中,执行过程中客户端从命令行中获得两个参数,第一个是要下载的文件名,第二个是要下载的机器的地址。
对应地址的机器上运行服务端。
代码例子4:
FileClient.java
importjava.io.*;
importjava.rmi.*;
publicclassFileClient{
publicstaticvoidmain(Stringargv[]){
if(argv.length!
=2){
System.out.println("Usage:
javaFileClientfileNamemachineName");
System.exit(0);
}
try{
Stringname="//"+argv[1]+"/FileServer";
FileInterfacefi=(FileInterface)Naming.lookup(name);
byte[]filedata=fi.downloadFile(argv[0]);
Filefile=newFile(argv[0]);
BufferedOutputStreamoutput=new
BufferedOutputStream(newFileOutputStream(file.getName()));
output.write(filedata,0,filedata.length);
output.flush();
output.close();
}catch(Exceptione){
System.err.println("FileServerexception:
"+e.getMessage());
e.printStackTrace();
}
}
}
运行程序
为了运行程序我们必须生成stubs和skeletons,为了生成stubs和skeletons,我们使用rmic来编译:
prompt>rmicFileImpl
将会生成两个文件FileImpl_Stub.class和FileImpl_Skel.class.stub是客户端的代理而skeleton是服务端的框架。
下一步是编译服务端和客户端。
使用javac来编译。
注意如果服务端和客户端在两个不同的机器,为了编译客户端你必须复制一个FileInterface接口。
最后,到你运行RMI注册工具和运行服务端和客户端的时候了。
使用rmiregistry或者startrmiregistry命令来运行RMI注册工具到window系统的默认的端口上,要运行RMI注册工具在一个其他的端口的话使用端口参数。
prompt>rmiregistryportNumber
RMI注册工具运行之后,你要运行服务FileServer,因为RMI的安全机制将在服务端发生作用,所以你必须增加一条安全策略。
以下是对应安全策略的例子
grant{
permissionjava.security.AllPermission"","";
};
注意:
这是一条最简单的安全策略,它允许任何人做任何事,对于你的更加关键性的应用,你必须指定更加详细安全策略。
现在为了运行服务端,你需要除客户类(FileClient.class)之外的所有的类文件。
确认安全策略在policy.txt文件之后,使用如下命令来运行服务器。
prompt>java-Djava.security.policy=policy.txtFileServer
为了在其他的机器运行客户端程序你需要一个远程接口(FileInterface.class)和一个stub(FileImpl_Stub.class)。
使用如下命令运行客户端
prompt>javaFileClientfileNamemachineName
这里fileName是要下载的文件名,machineName是要下载的文件所在的机器(也是服务端所在的机器)
如果所有都可以了话,当客户端运行后,这个文件将下载到本地。
我们提到如果要运行客户端,我们需要远程接口和stub,另外一个更好的方法是使用RMI动态类加载,这个方法使得你不必要复制远程接口和stub。
取而代之的是,它们能够在一个共同的目录里面被客户端和服务端查找到。
为了做到这个你必须使用以下命令来运行客户端。
使用如下命令
java-Djava.rmi.server.codebase=http:
//hostname/locationOfClassesFileClientfileNamemachineName.
更多的信息看这里DynamicCodeLoadingusingRMI.
CORBA
CORBA(TheCommonObjectRequestBrokerArchitecture:
通用对象请求代理结构)是对象管理组织(ORG)在分布式对象项目方面资助的一个工业标准。
CORBA只是一个标准,一个CORBA服务以ORB(ObjectRequestBroker对象要求代理)的形式来运行,市场上有很多可用的CORBAORB服务软件例如VisiBroker,ORBIX,和其他一些ORB软件。
JavaIDL是其中的一个,它是jdk1.3或jdk1.3以上版本的一个核心开发包。
CORBA的设计是独立于平台和语言的,因此CORBA可以在任何平台上运行,可以定位在网络的任何地方,能够使用任何有IDL(InterfaceDefinitionLanguage)映射的语言。
和RMI相类似,CORBA对象使用接口来描述,然而接口在CORBA中是定义在IDL中的。
IDL很类似于C++,但是IDL不是编程语言,更多的关于CORBA的介绍在这里有DistributedProgrammingwithJava:
Chapter11(OverviewofCORBA).
CROBA程序的编写过程
开发CORBA有很多复杂的步骤,如下
1.定义一个IDL
2.把IDL接口映射到java
3.开发server端
4.开发client端
5.运行名字服务,服务端和客户端
我们现在一步步的解释一个基corba的文件交换程序的开发,这有点类似于我们上面讲的RMI程序的开发,我们这里使用JavaIDL(一个jdk1.3的核心开发包).
定义接口
当你定义一个corba的接口时,考虑一下服务要支持的操作,在这个程序里面客户端将包含一个下载文件的方法。
代码例子5显示了一个FileInterface接口,Data是一个用typedef关键字引入的新的类型描述。
sequence在IDL中除了不能定义固定大小外其他都类似于数组,octet是一个8字节的量,相当于java中的byte。
downloadFile方法中的参数是一个string类型并被定义成in类型。
IDL定义了3中传输模式:
in(用来接收客户端到服务端的输入),out(用来接收服务端到客户端的输出)和inout(同时用来输入和输出)。
代码例子5:
FileInterface.idl
interfaceFileInterface{
typedefsequenceData;
DatadownloadFile(instringfileName);
};
一旦你定义了一个IDL接口,你就要开始编译它。
JDK1.3+包含了一个idlj编译器,它可以用来映射IDL到java的声明。
idlj可以通过不同的命令产生不同的输出如客户端的stubs,服务端的skeletons。
-f参数用来指定产生什么。
side是如下的client,server,或all参数分别用来表示客户端的stubs和服务端的skeletons。
在这个例子里由于服务端和客户端在两个不同的机器上,所以我们在客户端上使用-fclient在服务端上使用-fserver。
现在让我们编译FileInterface.idl来产生服务端的skeletons,使用如下命令
prompt>idlj-fserverFileInterface.idl
执行接口
现在我们提供一个对downloadFile方法的执行。
这个执行就像一个仆人,你可以从例子6中看出FileServant类从_FileInterfaceImplBase类扩展过来。
从这个类可以看出这个仆人是一个corba对象
例子6代码:
FileServant.java
importjava.io.*;
publicclassFileServantextends_FileInterfaceImplBase{
publicbyte[]downloadFile(StringfileName){
Filefile=newFile(fileName);
bytebuffer[]=newbyte[(int)file.length()];
try{
BufferedInputStreaminput=new
BufferedInputStream(newFileInputStream(fileName));
input.read(buffer,0,buffer.length);
input.close();
}catch(Exceptione){
System.out.println("FileServantError:
"+e.getMessage());
e.printStackTrace();
}
return(buffer);
}
}
开发服务端
下一步是编写corba服务端。
在例子7中实现了一个corba服务端的类FileServer。
它通过以下的步骤来实现。
1)定义一个orb
2)创建一个FileServant对象
3)登记到corba命名服务中(COSNaming)
4)打印状态消息
5)等待客户端请求
例子7代码:
FileServer.java
importjava.io.*;
importorg.omg.CosNaming.*;
importorg.omg.CosNaming.NamingContextPackage.*;
importorg.omg.CORBA.*;
publicclassFileServer{
publicstaticvoidmain(Stringargs[]){
try{
//createandinitializetheORB
ORBorb=ORB.init(args,null);
//createtheservantandregisteritwiththeORB
FileServantfileRef=newFileServant();
orb.connect(fileRef);
//gettherootnamingcontext
org.omg.CORBA.ObjectobjRef=
orb.resolve_initial_references("NameService");
NamingContextncRef=NamingContextHelper.narrow(objRef);
//Bindtheobjectreferenceinnaming
NameComponentnc=newNameComponent("FileTransfer","");
NameComponentpath[]={nc};
ncRef.rebind(path,fileRef);
System.out.println("Serverstarted....");
//Waitforinvocationsfromclients
java.lang.Objectsync=newjava.lang.Object();
synchronized(sync){
sync.wait();
}
}catch(Exceptione){
System.err.println("ERROR:
"+e.getMessage());
e.printStackTrace(System.out);
}
}
}
一旦FileServer获得一个orb,它能够登记corba服务。
它使用由omg建议的由JavaIDL实现的corba名字服务(COSNamingService)来登记。
它是目录服务中从根节点开始的一个目录节点。
这是一个普通的corba对象。
可以当成NamingContext对象来使用。
它必须能够被narroweddown(换句话说是分级(casted))
到相应的类型。
这是使用一个声明来实现的。
NamingContextncRef=NamingContextHelper.narrow(objRef);
ncRef对象现在是org.omg.CosNaming.NamingContext对象。
你可以使用它来登记一个corba服务。
rebind调用方法可以实现。
开发客户端
下一步是开发一个客户端,在例子8里面实现了,一旦得到了一个到名字服务的引用,它就能够被用来进入名字服务和能够用来查找一些服务(例如这里查找的是FileTransfer服务)当FileTransfer服务被找到的时候,downloadFile方法将被调用。
例子8代码:
FileClient
importjava.io.*;
importjava.util.*;
importorg.omg.CosNaming.*;
importorg.omg.CORBA.*;
publicclassFileClient{
publicstaticvoidmain(Stringargv[]){
try{
//createandinitializetheORB
ORBorb=ORB.init(argv,null);
//gettherootnamingcontext
org.omg.CORBA.ObjectobjRef=
orb.resolve_initial_references("NameService");
NamingContextncRef=NamingContextHelper.narrow(objRef);
NameCompon