內容概要:
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的架構圖,設計說明:Server端只需向一個登記處(Registry)為一個遠程對象注冊名字(唯一),然后就可以等待客戶端來使用了。客戶端要調用一個遠程方法前,先去登記處(Registry)look up(根據name),找到相應的對象(找到的是對象的引用),然后在客戶端進行方法調用。因為客戶端拿到的是遠程對象的引用,所以就像是直接調用一樣。
在有些情況下,客戶端會從服務端取一此Class的bytecode,服務端同樣也會從客戶端取一些類的bytecode。這樣做的前提是,對方那里有Web Server。獲取時使用都是URL協議:URL protocol (e.g., HTTP, FTP, file, etc.) 。
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,或者它的父類:IOException、Exception。
何時會拋出RemoteException?
1) 協議錯誤時
2) 通信失敗時(Server拒絕請求,或者沒有連接成功)
3) 參數或者接口在marshall、unmarshall過程中出現問題
在前面已經知道Registry是一個登記處。用于登記提供的遠程服務的。
Registry類提供了一些存儲、獲取遠程對象(在存儲時,遠程對象的引用會與一個name綁定,name是唯一的,所以獲取時可以根據name來獲取)。
提供了下列方法:
bind、unbind、rebind 用于將遠程對象與指定的name進行綁定和解綁。
list:用于列出已綁定的遠程對象的name。返回結果是數組。
另外有一點很重要,在Registry中存儲的是stub對象。而正在的對象是在RMI Server上。
當客戶端通過Naming或者Registry查找(loopup)到的都是對象的stub。然后利用Stub的序列化與反序列化功能進行Client與Server端通信。
LocateRegistry是用于定位Registry在網絡上的位置的,知道Registry在網絡上的位置,就能取得Registry對象,根據Registry對象就可以找到要使用的遠程對象,找到遠程對象就可以去調用遠程方法了。
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。
新聞熱點
疑難解答