Java RMI
Last updated
Last updated
Java RMI 用于不同虚拟机之间的通信,这些虚拟机可以在不同的主机上、也可以在同一个主机上;一个虚拟机中的对象调用另一个虚拟上中的对象的方法,只不过是允许被远程调用的对象要通过一些标志加以标识。
在RMI
中的核心是远程对象(remote object),除了对象本身所在的虚拟机,其他虚拟机也可以调用此对象的方法,而且这些虚拟机可以不在同一个主机上。每个远程对象都要实现一个或者多个远程接口来标识自己,声明了可以被外部系统或者应用调用的方法(当然也有一些方法是不想让人访问的)。
从方法调用角度来看,RMI
要解决的问题,是让客户端对远程方法的调用可以相当于对本地方法的调用而屏蔽其中关于远程通信的内容,即使在远程上,也和在本地上是一样的。
从客户端-服务器模型来看,客户端程序直接调用服务端,两者之间是通过JRMP
( Java Remote Method Protocol)协议通信,这个协议类似于HTTP协议,规定了客户端和服务端通信要满足的规范。
但是实际上,客户端只与代表远程主机中对象的Stub
对象进行通信,丝毫不知道Server
的存在。客户端只是调用Stub
对象中的本地方法,Stub
对象是一个本地对象,它实现了远程对象向外暴露的接口,也就是说它的方法和远程对象暴露的方法的签名是相同的。客户端认为它是调用远程对象的方法,实际上是调用Stub
对象中的方法。可以理解为Stub
对象是远程对象在本地的一个代理,当客户端调用方法的时候,Stub
对象会将调用通过网络传递给远程对象。
在java 1.2
之前,与Stub
对象直接对话的是Skeleton
对象,在Stub
对象将调用传递给Skeleton
的过程中,其实这个过程是通过JRMP
协议实现转化的,通过这个协议将调用从一个虚拟机转到另一个虚拟机。在Java 1.2
之后,与Stub
对象直接对话的是Server
程序,不再是Skeleton
对象了。
所以从逻辑上来看,数据是在Client
和Server
之间横向流动的,但是实际上是从Client
到Stub
,然后从Skeleton
到Server
这样纵向流动的。
我们都知道在Java
程序中引用类型(不包括基本类型)的参数传递是按引用传递的,对于在同一个虚拟机中的传递时是没有问题的,因为的参数的引用对应的是同一个内存空间,但是对于分布式系统中,由于对象不再存在于同一个内存空间,虚拟机A的对象引用对于虚拟机B没有任何意义。所以,需要通过序列化手段解决对象传输的问题。
在调用远程对象的方法之前需要一个远程对象的引用,如何获得这个远程对象的引用在是一个关键的问题。
服务端维护一个注册表(Registry),注册表中是<远程对象标识符, 远程对象>的映射关系,客户端通过提供远程对象的标识符访问注册表,来得到远程对象的引用。这个标识符是类似URL
地址格式的,它要满足的规范如下:
格式:rmi://host:port/name
,rmi是schema,host
指明注册表运行的主机,port
表明接收调用的端口,name
是一个标识该对象的简单名称。
主机和端口都是可选的,如果省略主机,则默认运行在本地;如果端口也省略,则默认端口是1099;
示例代码
编写一个RMI的步骤:
假如应用A有一个UserService,需要将其提供给其他应用调用:
UserSevice接口:需要继承Remote接口,表明是可供远程调用的