转载

使用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 san Hello, Zhang san 

RMI 架构

使用RMI进行分布式交互

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

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

RMI API

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来获取)。

提供了下列方法:

使用RMI进行分布式交互

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

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

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

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

LocateRegistry与Registry的关系

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

使用RMI进行分布式交互

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

提供的方法有:

使用RMI进行分布式交互

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

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

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

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

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

方式一: 使用Registry绑定或者获取一个远程对象的stub。  方式二: 直接使用Naming来绑定或者获取一个远程对象的stub。
正文到此结束
Loading...