一、远程方法调用
在分布式系统中,不同计算机上的进程需要互相通信以共同完成某一任务,完成分布式计算。远程方法调用(Remote Method Invocation,简称RMI)是一种通信机制,能够使得一个Java虚拟机上的对象能够调用另一个Java虚拟机上的对象上的方法。在RMI中,用户只需通过接口描述对象,而不需要对通信通道等底层细节担心,从而使得分布式计算更加方便和高效。
二、RMI的实现
RMI是在Java平台上基于CORBA的扩展,CORBA(Common Object Request Broker Architecture)是OMG(Object Management Group)开发的RPC机制。RMI可理解为Java语言上的RPC机制,其在实现上与CORBA有不少相似之处。
在Java RMI中,定义了客户端和服务器端之间远程调用的方法,通过Java RMI实现的远程调用,可使客户端程序调用服务程序中的方法,就像它们位于同一台计算机上一样。RMI的功能由Java语言与底层通信机制组成,这里我们将重点介绍RMI的技术实现。
三、RMI的核心技术
Java RMI具有下列三个主要特征:
1. 二进制编组
Java RMI用于远程方法调用的机制,采用的是对方法参数和返回值进行二进制编组的方式,以此实现数据的传输和序列化。这里选用的序列化方式是由Java序列化框架提供的对象序列化方式,即对象序列化流(ObjectOutputStream)和对象反序列化流(ObjectInputStream)。
2. 动态代理
Java RMI最有效的方式之一是通过Java代理实现远程对象的本地代理。客户端使用远程接口代理而不是直接访问远程对象;这样,RMI环境可以发现远程对象,尽管其已经在远程端都被封装了。在这种情况下,通信机制提供的是一个独立于实现的稳定接口,而不是一个不稳定的(和实现相关的)接口。
3. 注册表和发现服务
Java RMI为各进程之间提供了接口的查找、注册和发现机制。即:通过查找机制实现对远程对象的查找、注册机制实现对远程对象的注册、发现机制实现对远程对象的发现。
四、RMI实用技巧分享
1. 配置RMI的SecurityManager
在配置RMI时,应注意安全问题。为了避免任何外部进程在不被信任的情况下向RMI程序注入代码,需要使用Java的安全管理器机制。在服务器上,配置RMI的SecurityManager,可通过设置以下参数来启用:
```java
if (System.getSecurityManager ( ) == null) {
System.setSecurityManager ( new RMISecurity manager ( ) );
}
```
2. 导出Remote Objects
为确保远程对象能够被协议正确地传输,必须对其进行序列化。因此,所有的实现类和实例变量都必须实现Serializable序列化接口。实现类创建实例,然后通过调用UnicastRemoteObject.exportObject()方法将它们导出到RMI环境中。导入远程对象功能比较简单,如下:
```java
UnicastRemoteObject.exportObject ( this, port );
```
导出远程对象后,它应该被注册到RMI registry中,以便客户端可以通过注册表找到它。
3. 获取RMI Registry
获取RMI Registry的方式主要有以下两种:
- 直接创建Registry对象:通过将位置参数传递给Registry对象的构造函数来创建Registry对象。
- 查找Registry对象:在远程计算机上搜索registry对象,并获取通过java.rmi.Naming.lookup()方法注册的registry对象。
为了提高RMI的效率,可以使用RMI提供的downloadCodebase属性。该属性将可序列化的类注入到客户端,在远程计算机上执行并节省时间和网络带宽。
```java
if (System.getSecurityManager ( ) == null) {
System.setSecurityManager ( new RMISecurity manager ( ) );
}
try {
MyRemote server = ( MyRemote ) UnicastRemoteObject.exportObject ( new MyRemoteImp ( ), 9898 ); //导出远程对象
Registry registry = LocateRegistry.getRegistry ( ); //获取RMI注册表
registry.bind ( "MyRemote", server ); //绑定RMI注册表
System.out.println ( "Ready!" );
if ( server instanceof Remote ) {
System.out.println ( "System using Remote object" );
}
} catch ( RemoteException | AlreadyBoundException e ) {
e.printStackTrace ( );
}
```
本地化部署时,使用URLClassLoader下载Codebase时,请确保客户端的代码库设置正确。本地化部署时的problem occurs有可能是因为Codebase参数没设置导致的,可以通过下面的代码拦截问题:
```java
System.getProperties ( ).put ( "java.rmi.server.codebase", "file:/c:/workspace/MyRemote" );
System.getProperties ( ).put ( "java.rmi.server.logLevel", "VERBOSE" );
try {
System.setProperty ( "java.rmi.server.useCodebaseOnly", "true" );
MyRemote service = ( MyRemote ) Naming.lookup ( "rmi://localhost:9898/MyRemote" );
} catch ( RemoteException | NotBoundException | MalformedURLException e ) {
e.printStackTrace ( );
}
```
五、总结
RMI是Java平台上最常用的RPC机制之一,在分布式系统中,它是实现服务器与客户端通信的好工具。本文提到的配置Security、导出远程对象和获取RMI Registry、下载Codebase等技巧,是RMI程序员必不可少的技能。在完全掌握Java RMI技术之后,程序员可以使用其强大的功能与远程计算机进行通信,实现真正意义上的分布式计算。