用RMI進行遠程方法調用 |
|
遠程方法調用(RMI)機制可以把面向對象的思想進一步擴展,因為你可以調用的對象不僅可以在本機上,也可以在別的主機上。本文就簡單介紹rmi的編程方法。
首先介紹一些簡單的rmi的概念。 1, 服務器和客戶:在rmi中,如果有一個對象進行遠程方法調用,這個對象就叫做客戶機對象,而遠程對象則被稱為服務器對象。 2, 創建服務器對象的服務器程序:這個程序用來創建服務器對象,注冊這個對象,使得客戶可以通過注冊的名稱訪問服務器對象。 3, 接口(interface),接口可以讓客戶端了解服務器所能做的工作。更具體的說,就是它列出了可以在服務器上執行的所有方法。客戶端程序必須能夠找到這個類,否則就不能執行對服務器函數的調用。 4, 客戶樁(stub),有的書中翻譯成為代碼存根,它給客戶端程序提供一個樁,這個樁上"綁"著服務器對象。當客戶程序需要調用遠程對象時,這個樁被下載到客戶端(如果客戶端有這個類,則不需要下載)。然后客戶就可以像調用本地方法一樣調用遠程的方法了。 這個客戶樁的作用是將客戶向服務器的請求進行編碼、進行傳輸,服務器執行這次調用后將結果返回到客戶樁,客戶樁進行解碼,將解碼后的結果傳送到客戶程序中。對于編寫客戶端的程序員來說,他不需要知道其中的具體過程。 客戶樁不需要自己編寫,后面會說明它的生成方法。它實現了前述的接口(interface)。 下面就通過一個例子來說明編寫的過程。 1, 編寫服務器的接口:這一步是最主要的部分,因為接口是連接客戶機與服務器的關鍵部分。在這個例子中,接口很簡單,代碼如下: import java.rmi.*; public interface Product extends Remote { String getDescription() throws RemoteException; } 在這里應注意的是,遠程對象的接口一定要擴展(extend)Java.rmi包的Remote接口。同時接口中的所有的方法都要聲明拋出RemoteException異常。這是因為由于網絡連接的不可靠性,遠程方法調用很可能失敗。如果不聲明異常,在遠程方法調用失敗后,應用程序就會無法結束。 2, 編寫服務器對象: Java中具有一個可以直接使用的服務器類--UniCastRemoteObject。它存在于Java.rmi.server包中。我們可以直接擴展這個類,使它實現前述的接口。這樣就可以使服務器滿足我們的需要。 import java.rmi.server.*; import java.rmi.*; public class ProductImpl extends UnicastRemoteObject implements Product { public ProductImpl(String name) throws RemoteException { Desc = name; } public String getDescription() throws RemoteException { return "This is "+Desc+" product"; } private String Desc; } 可以看到,rmi服務器的實現和其他的方法代碼沒有什么不同。 3, 編寫創建服務器對象的服務器程序: import java.rmi.*; public class ProductServer { public static void main(String[] args) { try { System.out.println("Constructin Server implementations ...."); ProductImpl p1 = new ProductImpl("toaster"); ProductImpl p2 = new ProductImpl("microwave"); System.out.println("Binding server implementations to registry"); Naming.rebind("toaster",p1); Naming.rebind("microwave",p2); System.out.println("waiting for clients..."); }catch(Exception e) { System.out.println("Error "+e); } } } 通過代碼可以看到,這個服務器首先創建了兩個服務器對象。然后使用Naming.rebind()方法,將這個對象和一個名稱聯系(綁定)在一起。這個名稱就是客戶機查找服務器對象所使用的名稱。Naming是java.rmi包中的類。這個類的作用是建立一套查找對象的命名機制。通過它就可以將綁定在特定名稱上的對象找到。 4, 編寫客戶端代碼: import java.rmi.*; import java.rmi.server.*; public class ProductClient { public static void main(String[] args) { System.out.println("begin to invoke remote method"); System.setSecurityManager(new RMISecurityManager()); String url = "rmi://91.1.1.119:1099/"; try { file://查找遠程對象 System.out.println("1"); Product c1 = (Product)Naming.lookup(url + "toaster"); Product c2 = (Product)Naming.lookup(url + "microwave"); file://調用遠程方法 System.out.println("2"); System.out.println(c1.getDescription()); System.out.println("3"); System.out.println(c2.getDescription()); }catch (Exception ex) { System.out.println("error "+ex); } } } 在這段代碼中,首先定義了一個字符串url。這個字符串中存儲了找到遠程服務器對象的協議和地址信息。在rmi中,所使用的協議是rmi,端口號是1099。這個例子中,我的服務器對象存放在ip地址為91.1.1.119的主機上,所以,這個字符串的值為rmi://91.1.1.119:1099/。 接著,使用Naming.lookup()方法查找遠程對象。參數就是服務器的位置信息和服務器對象所綁定的名稱。 這里需要注意的是,通過lookup方法得到其實不是服務器對象本身的引用,而是下載到客戶機上的客戶樁。但是,這個方法得到的是Object類型,要使用這個對象,必須將它類型轉換成服務器所實現的接口類型。 隨后,就可以像調用本地方法一樣調用遠程方法。在這個例子中,遠程方法是getDescription()。 因為這段代碼是對遠程對象進行操作,所以,它被放到一個try…catch塊中,來捕獲遠程調用過程中的異常。 最后,就要將服務器和客戶機部署到機器上。 1,將所有的類文件編譯為class文件。然后在dos方式下使用 rmic ProductImpl 就可以生成客戶樁,名為ProductImpl_Stub.class。 2,把客戶端代碼和接口代碼拷貝到客戶機器上。 3,運行rmiregistry程序,啟動注冊系統,使得服務器可以注冊在機器上,以供客戶調用。 4,啟動http服務。將接口類和客戶樁類放在http服務器上,使得客戶可以下載。假設這兩個文件的下載目錄是http://91.1.1.119/download/ 5,使用start java -Djava.rmi.server.codebase= http://91.1.1.119/download/ ProductServer 運行創建服務器對象的程序。 其中的-Djava.rmi.server.codebase= http://91.1.1.119/download/ 指明客戶程序下載客戶樁的地址。 6,因為rmi有安全限制,所以在客戶端必須建立一個策略文件。假設名為client.policy 文件的內容為 grant { permission java.net.SocketPermission "91.1.1.119:1024-65535","connect"; permission java.net.SocketPermission "91.1.1.119:80","connect"; }; 使用 start java -Djava.security.policy=client.policy ProductClient 啟動客戶端,客戶端就可以連接80端口(http端口)和1024-65535的端口(其中包含了rmi的缺省端口1099)。之后就可以看到程序的執行結果。 以上就是使用rmi進行遠程方法調用的基本過程。 但是,應該注意到,rmi有一個很大的限制,那就是只能在java編寫的對象之間使用,如果要在不同的語言寫成的對象之間通訊,那就需要CORBA的幫助 |