标签:
RMI(Remote Method Invocation)是JAVA早期版本(JDK 1.1)提供的分布式应用解决方案,它作为重要的API被广泛的应用在EJB中。随着互联网应用的发展,分布式处理任务也随之复杂起 来,WebService也得到普遍的推广和应用。
在某些方面,例如跨语言平台的分布式应用,RMI就显得力不从心了。在实际的应用中,是采用WebService还是传统的RMI来实现?这是一个需要权衡的问题,两者的比较如下所述:
1. 比起WebService,它只能使用(调用)由JAVA编写的远程服务。而WebService是跨语言平台的,只要客户端和服务端双方都遵守SOAP规范即可;
2. RMI是在TCP协议基础上传递可序列化的java对象(字节数据),而WebService是在HTTP协议基础上通过XML来传输数据的。因此,在同等业务数据量的前提下,RMI的效率要高于WebService。
因此,RMI可以用在业务结构比较简单,而要求实时高效的分布式应用中。
从设计角度上讲,JAVA采用的是三层结构模式来实现RMI。在整个体系结构中,有如下几个关键角色构成了通信双方:
1.客户端:
1)桩(StubObject):远程对象在客户端上的代理;
2)远程引用层(RemoteReference Layer):解析并执行远程引用协议;
3)传输层(Transport):发送调用、传递远程方法参数、接收远程方法执行结果。
2.服务端:
1)骨架(Skeleton):读取客户端传递的方法参数,调用服务器方的实际对象方法,并接收方法执行后的返回值;
2)远程引用层(Remote ReferenceLayer):处理远程引用语法之后向骨架发送远程方法调用;
3)传输层(Transport):监听客户端的入站连接,接收并转发调用到远程引用层。
3.注册表(Registry):以URL形式注册远程对象,并向客户端回复对远程对象的引用。
图1 RMI三层模型
在实际的应用中,客户端并没有真正的和服务端直接对话来进行远程调用,而是通过本地JVM环境下的桩对象来进行的。
1.远程调用过程:
1)客户端从远程服务器的注册表中查询并获取远程对象引用。当进行远程调用时,客户端首先会与桩对象(Stub Object)进行对话,而这个桩对象将远程方法所需的参数进行序列化后,传递给它下层的远程引用层(RRL);
2)桩对象与远程对象具有相同的接口和方法列表,当客户端调用远程对象时,实际上是由相应的桩对象代理完成的。远程引用层在将桩的本地引用转换为服务器上对象的远程引用后,再将调用传递给传输层(Transport),由传输层通过TCP协议发送调用;
3)在服务器端,传输层监听入站连接,它一旦接收到客户端远程调用后,就将这个引用转发给其上层的远程引用层;
4)服务器端的远程引用层将客户端发送的远程应用转换为本地虚拟机的引用后,再将请求传递给骨架(Skeleton);
5)骨架读取参数,又将请求传递给服务器,最后由服务器进行实际的方法调用。
2.结果返回过程:
1)如果远程方法调用后有返回值,则服务器将这些结果又沿着“骨架->远程引用层->传输层”向下传递;
2)客户端的传输层接收到返回值后,又沿着“传输层->远程引用层->桩”向上传递,然后由桩来反序列化这些返回值,并将最终的结果传递给客户端程序。
又从技术的角度上讲,有如下几个主要类或接口扮演着上述三层模型中的关键角色:
1)注册表:java.rmi.Naming和ava.rmi.Registry;
2)骨架:java.rmi.remote.Skeleton
3)桩:java.rmi.server.RemoteStub
4)远程引用层:java.rmi.server.RemoteRef和sun.rmi.transport.Endpoint;
5)传输层:sun.rmi.transport.Transport
作为一般的RMI应用,JAVA为我们隐藏了其中的处理细节,而让开发者有更多的精力和时间花在实际的应用中。开发RMI的步骤如下所述:
1.服务端:
1)定义Remote子接口,在其内部定义要发布的远程方法,并且这些方法都要Throws RemoteException;
2)定义远程对象的实现类,通常有两种方式:
a. 继承UnicastRemoteObject或Activatable,并同时实现Remote子接口;
b. 只实现Remote子接口和java.io.Serializable接口。
3)编译桩(在JAVA 1.5及以后版本中,如果远程对象实现类继承了UnicastRemoteObject或Activatable,则无需此步,由JVM自动完成。否则需手工利用rmic工具编译生成此实现类对应的桩类,并放到和实现类相同的编译目录下);
4)启动服务器:依次完成注册表的启动和远程对象绑定。另外,如果远程对象实现类在定义时没有继承UnicastRemoteObject或Activatable,则必须在服务器端显示的调用UnicastRemoteObject类中某个重载的exportObject(Remote remote)静态方法,将此实现类对象导出成为一个真正的远程对象。
2.客户端:
1)定义用于接收远程对象的Remote子接口,只需实现java.rmi.Remote接口即可。但要求必须与服务器端对等的Remote子接口保持一致,即有相同的接口名称、包路径和方法列表等。
2)通过符合JRMP规范的URL字符串在注册表中获取并强转成Remote子接口对象;
3)调用这个Remote子接口对象中的某个方法就是为一次远程方法调用行为。
下面结合一个例子来说明RMI分布式应用的开发步骤。
背景:远程系统管理接口SystemManager允许客户端远程调用其内部的某个方法,来获取服务器环境下某个属性的值。因此,第一步需要在服务端和与之通信的客户端环境下,定义一个完全一样的SystemManager接口,将此接口标记为远程对象。
1.在服务端和客户端定义对等Remote子接口(SystemManager)
2.在服务端定义Remote子接口的实现类(SystemManagerImpl),即远程对象的实际行为逻辑。
3.由于SystemManagerImpl 不是通过继承UnicastRemoteObject 或 Activatable来实现的,因此在服务器端需要利用JDK提供的rmic工具编译生成对应的桩类。否则,此步略过。例如,在Windows环境下打开命令控制台后
1)进入工程根目录 :
cd d:\ Development\AppDemo
2)桩编译:
cmic -classpath WebContent\WEB-INF\classes com.daniele.appdemo.rmi.server.SystemManagerImpl
语法格式为:
cmic -classpath <远程对象实现类bin目录的相对路径> <远程对象实现类所在的包路径>.<远程对象实现类的名称>
完成后,rmic将在相对于根目录的com\daniele\appdemo\rmi\server子目录中自动生成SystemManagerImpl_Stub桩对象类 (即“远程对象实现类名称_Stub”) 的编译文件,此时需要再将此编译文件拷贝到与远程对象实现类SystemManagerImpl相同的编译目录(\WebContent\WEB-INF\classes\com\daniele\appdemo\rmi\server)中,否则在服务器端发布远程对象时将会抛出java.rmi.StubNotFoundException。如下图所示。
图2 实现类与桩
需要特别注意的是:如果服务端中的远程对象实现类存在有对应的桩对象类编译文件,则要求在RMI客户端的环境中,也必须有这个对等的桩对象类编译文件,即意味着这个文件在两端有着相同的包路径、文件名和内部实现细节。因此,最简单的做法就是连同整个包(com\daniele\appdemo\rmi\server)在内,将图2中的SystemManagerImpl_Stub.class文件拷贝到RMI客户端工程的bin目录下即可。如下图。否则,当RMI客户端调用远程服务时将会抛出java.rmi.StubNotFoundException。
图3 RMI客户端工程下的对等桩文件
4.创建用于发布远程对象目的用的服务器(SystemManagerServer)
5.创建发出远程调用请求的客户端(SystemManagerClient)
标签:
原文地址:http://www.cnblogs.com/wangyonglong/p/5979049.html