你的位置:首页 > Java教程

[Java教程]使用RMI进行分布式交互


内容概要

  • RMI 示例
  • RMI 架构
  • RMI API
  1. Remote    
  2. RemoteException
  3. Registry, LocateRegistry
  4. Naming

 

 

RMI 示例

 在了解RMI之前,先来看一个例子:

// 服务接口package com.fjn.java.rmi.quickstart.server;import java.rmi.Remote;import java.rmi.RemoteException;public interface Hello extends Remote{  public String sayHello(String str) throws RemoteException;}// 服务实现package com.fjn.java.rmi.quickstart.server;import java.io.Serializable;import java.rmi.RemoteException;public class HelloImpl implements Hello, Serializable {  protected HelloImpl() throws RemoteException {    super();  }  private static final long serialVersionUID = 3556503295294925414L;  @Override  public String sayHello(String str) {    return "Hello, "+str;  }} // Server端发布服务:package com.fjn.java.rmi.quickstart.server;import java.net.MalformedURLException;import java.rmi.AlreadyBoundException;import java.rmi.NotBoundException;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;import java.rmi.server.UnicastRemoteObject; public class ServerTest {  public static void main(String[] args) throws RemoteException, AlreadyBoundException, InterruptedException, MalformedURLException, NotBoundException {    // 创建Registry    Registry registry=LocateRegistry.createRegistry(9998);      Hello stub=(Hello) UnicastRemoteObject.exportObject(new HelloImpl(),0);    // 将ref绑定到registry中    registry.rebind("Hello", stub);    Hello stub2=(Hello)registry.lookup("Hello");    System.out.println(stub2);    Thread.sleep(1000*60*60);  }}

执行结果:

Hello, zhang sanHello, Zhang san

 

RMI 架构

 

       上面的这个图表示了RMI的架构图,设计说明:Server端只需向一个登记处(Registry)为一个远程对象注册名字(唯一),然后就可以等待客户端来使用了。客户端要调用一个远程方法前,先去登记处(Registry)look up(根据name),找到相应的对象(找到的是对象的引用),然后在客户端进行方法调用。因为客户端拿到的是远程对象的引用,所以就像是直接调用一样。

        在有些情况下,客户端会从服务端取一此Class的bytecode,服务端同样也会从客户端取一些类的bytecode。这样做的前提是,对方那里有Web Server。获取时使用都是URL协议:URL protocol (e.g., HTTP, FTP, file, etc.) 。

 

RMI API

Remote

The Remote interface serves to identify interfaces whose methods may be invoked from a non-local virtual machine. Any object that is a remote object must directly or indirectly implement this interface. Only those methods specified in a "remote interface", an interface that extends java.rmi.Remote are available remotely.

       在这个接口中,没有任何方法。这个接口只是一个标记作用,标记那些可以在非本地使用的接口。也就是说,如果这个接口可以被RMI客户端使用,就必须extends这个接口,或者Remote的子接口。

 将实现了Remote接口的对象称为远程对象。RMI用于分布式程序,因此也将远程对象称为分布式对象。

 

远程接口的设计原则

1)必须继承Remote接口

2)在远程接口中声明的方法必须满足下列要求:

       A:方法必须抛出java.rmi.RemoteException(IOException,Exception也可以,因为这两个异常是RemoteException的父类)。

       B:远程方法的声明中:如果远程对象出现参数或者返回值中,那么得使用远程接口本身。

3)远程接口如果也继承了其他的非远程接口,只要远程方法满足2)中的要求即可。作为远程接口本身,要继承Remote接口,在这个远程接口中直接声明的方法要满足2)中的要求。从非远程接口中继承来的方法也得满足2)中的要求。

4)接口的实现,必须实现Serializable接口。

 

RemoteException

       为了保证远程调用时,程序的正常进行,需要对每一个远程方法都抛出RemoteException,或者它的父类:IOException、Exception。

 

何时会抛出RemoteException?

1)  协议错误时

2)  通信失败时(Server拒绝请求,或者没有连接成功)

3)  参数或者接口在marshall、unmarshall过程中出现问题

 

Registry、LocateRegistry

 

在前面已经知道Registry是一个登记处。用于登记提供的远程服务的。

Registry类提供了一些存储、获取远程对象(在存储时,远程对象的引用会与一个name绑定,name是唯一的,所以获取时可以根据name来获取)。

 

提供了下列方法:

 

 bind、unbind、rebind 用于将远程对象与指定的name进行绑定和解绑。

list:用于列出已绑定的远程对象的name。返回结果是数组。

另外有一点很重要,在Registry中存储的是stub对象。而正在的对象是在RMI Server上。

当客户端通过Naming或者Registry查找(loopup)到的都是对象的stub。然后利用Stub的序列化与反序列化功能进行Client与Server端通信。

 

LocateRegistry与Registry的关系

LocateRegistry是用于定位Registry在网络上的位置的,知道Registry在网络上的位置,就能取得Registry对象,根据Registry对象就可以找到要使用的远程对象,找到远程对象就可以去调用远程方法了。

 

Naming

 

Naming类提供了一些存储和获取远程对象(存储在Registry中的)的引用的方法。

提供的方法有:

 

 

提供的方法中,都有一个name参数,是字符串类型的。它是有一个标准的格式(是一个URL,但是少了URL中的schema部分,schema是rmi)。格式为:

//host:port/name 或者 rmi://host:port/name

 

Host就是定位到的Registry的host,端口也是。

从提供的方法上来看,Naming就是一个工具类,它提供的方法都是静态的,但是方法确实和Registry中的方法是一样的。如此,目前就学习了两种获取、绑定远程对象的方法了。

也就是说Registry在客户端和服务端都是可用的。绑定用于服务端,获取用于客户端。

 

方式一:使用Registry绑定或者获取一个远程对象的stub。方式二:直接使用Naming来绑定或者获取一个远程对象的stub。