java远程方法调用 Remote Method Invocation
#定义
Java RMI 指的是远程方法调用 (Remote Method Invocation)。它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法。可以用此方法调用的任何对象必须实现该远程接口。
如何调用另一个JVM中的对象的方法?
由于不能取得另一个堆的对象的引用,换句话说,不可以这样写:
变量d只能引用当前代码语句的同一堆空间的对象.那怎么办?该是Java远程方法调用出现的时刻了…..RMI可以让我们找到远程JVM内的对象,并允许我们调用它们的方法.
假如我们要设计一个系统,能够调用本地对象,然后将每个请求转发到远程对象上进行.要如何设计?我们需要一些辅助对象,帮我们真正进行沟通.这些辅助对象使客户就像在调用本地对象的方法(事实也如此)一样.客户调用客户辅助对象上的方法,仿佛客户辅助对象就是真正的服务.客户辅助对象再负责为我们转发这些请求.
换句话说,客户对象以为它调用的是远程服务上的方法,因为客户辅助对象乔装成服务对象,假装自己有客户所要调用的方法.
但是客户辅助对象并不是真正的远程服务.虽然操作看起来很像(因为具有服务所宣称的相同的方法),但是并不真正拥有客户所期望的方法罗辑.客户辅助对象会联系服务器,传送方法调用信息(例如,方法名称,变量),然后等待服务器的返回.
在服务器端,服务辅助对象从客户辅助对象中接受请求(透过Socket连接),将调用的信息解包,然后调用真正服务对象上的真正方法.所以,对于服务对象来说,调用是本地的,来自服务辅助对象,而不是远程客户.
服务辅助对象从服务中得到返回值,将他打包,然后运回到客户辅助对象(通过网络Socket的输出流),客户辅助对象对信息解包,最后将返回值交给客户对象.
#方法调用是如何发生的
RMI提供了 客户辅助对象和服务辅助对象,为客户辅助对象创建和服务对象相同的方法.RMI的好处在于你不必亲自写任何网络或IO代码.客户程序调用远程方法就和运行在客户自己的本地JVM上对对象进行正常方法调用一样.
#制作远程服务
- 步骤1:制作远程接口
远程接口定义出可以让客户远程调用的方法.客户将用它作为服务的类类型.
Stub和实际的服务都实现自此接口. - 步骤2:制作远程实现
这是做实际工作的类,为远程接口中定义的远程方法提供了真正的实现.这就是客户真正想要调用方法的对象. - 步骤3:利用rmic产生的stub和skeleton
这就是客户和服务的辅助类.你不许自己创建这些类,甚至连生成他们的代码都不用看,因为当你在运行rmic工具时,这都会自动处理.你可以在jdk中找到rmic
- 步骤4:启动RMI registry(rmiregistry)
rmiregistry就像是电话本,客户可以从中查到代理的位置 - 步骤5:开始远程服务
你必须让服务对象开始运行.你的服务实现类会去实例化一个服务的实例,并将这个服务注册到RMI registry.注册之后,这个服务就可以提供客户调用了.
###1.制作远程接口
扩展java.rmi.Remote:Remote是一个记号接口,所以Remote不具有方法.对于RMI来说,Remote接口具有特别的意义,所以我们必须遵守规则.
1public interface MyRemote extends Remote{}声明所有的方法都会抛出RemoteException
1234import java.rmi.*;public interface MyRemote extends Remote{public String method() throws RemoteException;}确定变量和返回值是属于原语(primitive)类型或者可以序列化(Serializable)类型
远程方法的变量和返回值,必须属于原语类型或Serializable类型.这不难理解.远程方法的变量必须被打包并通过网络运送,这要靠序列化来完成.如果你使用原语类型,字符串和许多API中内定的类型(包括数组和集合),都不会有问题.如果你传送自己定义的类,就必须保证你的类实现了Serializable.12public String//这个返回值将从服务器通过网络运回给客户,所以必须是Serializable的.这样才可以将变量和返回值打包并传送.method() throws RemoteException;
###2.制作远程实现
实现远程接口,扩展UnicastRemoteObject
为了要成为远程服务对象,你的对象需要某些”远程的”功能.最简单的方式是扩展java.rmi.server.UnicastRemoteObject,让超类帮你做这些工作.1public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{}设计一个无参数的构造器,并声明RemoteException;
你的新超类UnicastRemoteObject带来一个小问题:它的构造器抛出一个RemoteExceoption;解决办法就是子类的构造器也抛出异常;1public MyremoteImpl() throws RemoteException{}用RMI Registry注册此服务 :
现在已经有了一个远程服务了,必须让它可以被远程客户调用.你要做的是将此服务实例化,然后放进RMI registry中.当注册这个实现对象时,RMI系统其实注册的是Stub,因为这是客户真正需要的.注册服务使用了java.rmi.Naming类的静态rebind()方法;1234try{MyRemote service = new MyRemoteImpl();Naming.rebind("RemoteMethod",service);} catch(Exception ex){...}
###3.产生Stub和Skeleton
rmic 是jdk内置的一个工具,用来为一个服务类产生stub和skeleton.命名习惯是在远程实现的名字后面加上_stub或者_Skel.
###4.执行remiregistry
开启一个终端,启动rmiregistry
先确定启动目录必须可以访问你的类.最简单的做法是从你的’.class’所在目录启动
###5.启动服务
开启另一个终端,启动服务
从哪里启动?可能是从你的远程实现类中的main()方法中,也可能是从一个独立的启动类;