RMI 说明
見:https://baike.baidu.com/item/RMI/1786244?fr=aladdin
RMI遠程方法調用
相關概述
RMI系統(tǒng)運行機理
RMI應用程序通常包括兩個獨立的程序:服務器程序和客戶機程序。典型的服務器應用程序將創(chuàng)建多個遠程對象,使這些遠程對象能夠被引用,然后等待客戶機調用這些遠程對象的方法。而典型的客戶機程序則從服務器中得到一個或多個遠程對象的引用,然后調用遠程對象的方法。RMI為服務器和客戶機進行通信和信息傳遞提供了一種機制。 在與遠程對象的通信過程中,RMI使用標準機制:stub和skeleton。遠程對象的stub擔當遠程對象的客戶本地代表或代理人角色。調用程序將調用本地stub的方法,而本地stub將負責執(zhí)行對遠程對象的方法調用。在RMI中,遠程對象的stub與該遠程對象所實現(xiàn)的遠程接口集相同。調用stub的方法時將執(zhí)行下列操作: (1) 初始化與包含遠程對象的遠程虛擬機的連接; (2) 對遠程虛擬機的參數(shù)進行編組(寫入并傳輸); (3) 等待方法調用結果; (4) 解編(讀取)返回值或返回的異常; (5) 將值返回給調用程序。為了向調用程序展示比較簡單的調用機制,stub將參數(shù)的序列化和網(wǎng)絡級通信等細節(jié)隱藏了起來。在遠程虛擬機中,每個遠程對象都可以有相應的skeleton(在JDK1.2環(huán)境中無需使用skeleton)。Skeleton負責將調用分配給實際的遠程對象實現(xiàn)。它在接收方法調用時執(zhí)行下列操作:(1) 解編(讀取)遠程方法的參數(shù);(2) 調用實際遠程對象實現(xiàn)上的方法;(3) 將結果(返回值或異常)編組(寫入并傳輸)給調用程序。stub和skeleton由rmic編譯器生成。 利用RMI編寫分布式對象應用程序需要完成以下工作:(1) 定位遠程對象。應用程序可使用兩種機制中的一種得到對遠程對象的引用。它既可用RMI的簡單命名工具rmiregistry來注冊它的遠程對象,也可以將遠程對象引用作為常規(guī)操作的一部分來進行傳遞和返回。(2)與遠程對象通信。遠程對象間通信的細節(jié)由RMI處理,對于程序員來說,遠程通信看起來就像標準的Java方法調用。(3)給作為參數(shù)或返回值傳遞的對象加載類字節(jié)碼。因為RMI允許調用程序將純Java對象傳給遠程對象,所以,RMI將提供必要的機制,既可以加載對象的代碼又可以傳輸對象的數(shù)據(jù)。在RMI分布式應用程序運行時,服務器調用注冊服務程序以使名字與遠程對象相關聯(lián)。客戶機在服務器上的注冊服務程序中用遠程對象的名字查找該遠程對象,然后調用它的方法。系統(tǒng)組成
一個正常工作的RMI系統(tǒng)由下面幾個部分組成: ·遠程服務的接口定義 ·遠程服務接口的具體實現(xiàn) ·樁(Stub)和框架(Skeleton)文件 ·一個運行遠程服務的服務器 ·一個RMI命名服務,它允許客戶端去發(fā)現(xiàn)這個遠程服務 ·類文件的提供者(一個HTTP或者FTP服務器) ·一個需要這個遠程服務的客戶端程序技術原理
RMI系統(tǒng)結構,在客戶端和服務器端都有幾層結構。 --------- ---------- | 客戶 | |服務器| ---------- ---------- | | ------------- ---------- | 占位程序| |骨干網(wǎng)| -------------- ----------- | | ------------------------------------ | 遠 程 引 用 層 | ------------------------------------ | | ------------------------------------ | 傳 輸 層 | ------------------------------------ 方法調用從客戶對象經(jīng)占位程序(Stub)、遠程引用層(Remote Reference Layer)和傳輸層(Transport Layer)向下,傳遞給主機,然后再次經(jīng)傳 輸層,向上穿過遠程調用層和骨干網(wǎng)(Skeleton),到達服務器對象。 占位程序扮演著遠程服務器對象的代理的角色,使該對象可被客戶激活。 遠程引用層處理語義、管理單一或多重對象的通信,決定調用是應發(fā)往一個服務器還是多個。傳輸層管理實際的連接,并且追追蹤可以接受方法調用的遠程對象。服務器端的骨干網(wǎng)完成對服務器對象實際的方法調用,并獲取返回值。返回值向下經(jīng)遠程引用層、服務器端的傳輸層傳遞回客戶端,再向上經(jīng)傳輸層和遠程調用層返回。最后,占位程序獲得返回值。 要完成以上步驟需要有以下幾個步驟: 1、生成一個遠程接口 2、實現(xiàn)遠程對象(服務器端程序) 3、生成占位程序和骨干網(wǎng)(服務器端程序) 4、編寫服務器程序 5、編寫客戶程序 6、注冊遠程對象 7、啟動遠程對象具體實現(xiàn)如下: 1、生成一個遠程接口 package c15.ptime; importjava.rmi.*; public interface PerfectTimeI extends Remote { long getPerfectTime() throws RemoteException; } 2、實現(xiàn)遠程對象(服務器端程序) package c15.ptime; import java.rmi.*; importjava.rmi.server.*; importjava.rmi.registry.*; import java. net.*; public class PerfectTime extends UnicastRemoteObject implements PerfectTimeI { public long getPerfectTime() throws RemoteException { return System.currentTimeMillis(); } public PerfectTime() throws RemoteException { super(); } public static void main(String[] args) { try { PerfectTime pt = new PerfectTime(); LocateRegistry.createRegistry(2005); Naming.rebind( "//zhouty:2005/PerfectTime" , pt); System.out.println("Ready to do time"); } catch(Exception e) { e.printStackTrace(); } } } 4、編譯遠程對象(服務器端程序) javac -classpath . -d . PerfectTime.java 5、生成根和干(占位程序和骨干程序) rmic -classpath . -d . c15.ptime.PerfectTime 注:jdk1.2以后的都不需要skeleton,所以如果你用的jdk為5.0版本的, 不要奇怪為什么只產生了stub沒有skeleton。 6、注冊遠程對象 start rmiregistry 2005 注:綁定服務的默認端口為1099,如果使用了這個端口,則可以直接使用 start rmiregistry而不需要跟端口 如果這種注冊遠程對象的方法不起作用. 還有一種方法就是在綁定服務之前使用LocateRegistry.createRegistry(1099) 來注冊遠程對象. 7、啟動服務器端程序 java-Djava.rmi.server.codebase=file:///d:/TestRMI/ c15.ptime.Per fectTime 8、編寫客戶端程序 package c15.ptime; import java.rmi.*; importjava.rmi.registry.*; public class DisplayPerfectTime { public static void main(String[] args) { System.setSecurityManager( new RMISecurityManager()); try { PerfectTimeI t = (PerfectTimeI)Naming.lookup( "192.168.0.171:2005/PerfectTime"); for(int i = 0 ; i < 10; i++) System.out.println("Perfect time =" + t.getPerfectTime()); } catch(Exception e) { e.printStackTrace(); } } } 9、編譯客端程序 javac -classpath . -d . DisplayPerfectTime.java 10、修改JVM的配置文件 (客戶機和服務器的都需要經(jīng)過修改) %JRE_HOME%\policytool.exe 11、啟動客戶程序 java -classpath . c15.ptime.DisplayPerfectTime 12、返回結果 Perfect time =967274884390 Perfect time =967274884450 Perfect time =967274884450 Perfect time =967274884450 Perfect time =967274884500 Perfect time =967274884500 Perfect time =967274884560 Perfect time =967274884610 Perfect time =967274884610 Perfect time =967274884610
RMI(遠程方法調用)的優(yōu)點
從最基本的角度看,RMI是Java的遠程過程調用(RPC)機制。與傳統(tǒng)的RPC系統(tǒng)相比,RMI具有若干優(yōu)點,因為它是Java面向對象方法的一部分。傳統(tǒng)的RPC系統(tǒng)采用中性語言,所以是最普通的系統(tǒng)--它們不能提供所有可能的目標平臺所具有的功能。 RMI以Java為核心,可與采用本機方法與現(xiàn)有系統(tǒng)相連接。這就是說,RMI可采用自然、直接和功能全面的方式為您提供分布式計算技術,而這種技術可幫助您以不斷遞增和無縫的方式為整個系統(tǒng)添加Java功能。 RMI的主要優(yōu)點如下: 面向對象:RMI可將完整的對象作為參數(shù)和返回值進行傳遞,而不僅僅是預定義的數(shù)據(jù)類型。也就是說,您可以將類似Java哈希表這樣的復雜類型作為一個參數(shù)進行傳遞。而在目前的RPC系統(tǒng)中,您只能依靠客戶機將此類對象分解成基本數(shù)據(jù)類型,然后傳遞這些數(shù)據(jù)類型,最后在服務器端重新創(chuàng)建哈希表。RMI則不需額外的客戶程序代碼(將對象分解成基本數(shù)據(jù)類型),直接跨網(wǎng)傳遞對象。 可移動屬性:RMI可將屬性(類實現(xiàn)程序)從客戶機移動到服務器,或者從服務器移到客戶機。例如,您可以定義一個檢查雇員開支報告的接口,以便察看雇員是否遵守了公司目前實行的政策。在開支報告創(chuàng)建后,客戶機就會從服務器端獲得實現(xiàn)該接口的對象。如果政策發(fā)生變化,服務器端就會開始返回使用了新政策的該接口的另一個實現(xiàn)程序。您不必在用戶系統(tǒng)上安裝任何新的軟件就能在客戶端檢查限制條件--從而向用戶提供爍快的反饋,并降低服務器的工作量。這樣就能具備最大的靈活性,因為政策改變時只需要您編寫一個新的Java類,并將其在服務器主機上安裝一次即可。 設計方式:對象傳遞功能使您可以在分布式計算中充分利用面向對象技術的強大功能,如二層和三層結構系統(tǒng)。如果您能夠傳遞屬性,那么您就可以在您的解決方案中使用面向對象的設計方式。所有面向對象的設計方式無不依靠不同的屬性來發(fā)揮功能,如果不能傳遞完整的對象--包括實現(xiàn)和類型--就會失去設計方式上所提供的優(yōu)點。 安 全:RMI使用Java內置的安全機制保證下載執(zhí)行程序時用戶系統(tǒng)的安全。RMI使用專門為保護系統(tǒng)免遭惡意小應用程序侵害而設計的安全管理程序,可保護您的系統(tǒng)和網(wǎng)絡免遭潛在的惡意下載程序的破壞。在情況嚴重時,服務器可拒絕下載任何執(zhí)行程序。 便于編寫和使用:RMI使得Java遠程服務程序和訪問這些服務程序的Java客戶程序的編寫工作變得輕松、簡單。遠程接口實際上就是Java接口。服務程序大約用三行指令宣布本身是服務程序,其它方面則與任何其它Java對象類似。這種簡單方法便于快速編寫完整的分布式對象系統(tǒng)的服務程序,并快速地制做軟件的原型和早期版本,以便于進行測試和評估。因為RMI程序編寫簡單,所以維護也簡單。 可連接現(xiàn)有/原有的系統(tǒng):RMI可通過Java的本機方法接口JNI與現(xiàn)有系統(tǒng)進行進行交互。利用RMI和JNI,您就能用Java語言編寫客戶端程序,還能使用現(xiàn)有的服務器端程序。在使用RMI/JNI與現(xiàn)有服務器連接時,您可以有選擇地用Java重新編寫服務程序的任何部分,并使新的程序充分發(fā)揮Java的功能。類似地,RMI可利用JDBC、在不修改使用數(shù)據(jù)庫的現(xiàn)有非Java源代碼的前提下與現(xiàn)有關系數(shù)據(jù)庫進行交互。 編寫一次,到處運行:RMI是Java“編寫一次,到處運行 ”方法的一部分。任何基于RMI的系統(tǒng)均可100%地移植到任何Java虛擬機上,RMI/JDBC系統(tǒng)也不例外。如果使用RMI/JNI與現(xiàn)有系統(tǒng)進行交互工作,則采用JNI編寫的代碼可與任何Java虛擬機進行編譯、運行。 分布式垃圾收集:RMI采用其分布式垃圾收集功能收集不再被網(wǎng)絡中任何客戶程序所引用的遠程服務對象。與Java虛擬機內部的垃圾收集類似,分布式垃圾收集功能允許用戶根據(jù)自己的需要定義服務器對象,并且明確這些對象在不再被客戶機引用時會被刪除。 并行計算:RMI采用多線程處理方法,可使您的服務器利用這些Java線程更好地并行處理客戶端的請求。Java分布式計算解決方案:RMI從JDK 1.1開始就是Java平臺的核心部分,因此,它存在于任何一臺1.1 Java虛擬機中。所有RMI系統(tǒng)均采用相同的公開協(xié)議,所以,所有Java 系統(tǒng)均可直接相互對話,而不必事先對協(xié)議進行轉換。RMI與CORBA的關系
RMI 和 CORBA 常被視為相互競爭的技術,因為兩者都提供對遠程分布式對象的透明訪問。但這兩種技術實際上是相互補充的,一者的長處正好可以彌補另一者的短處。RMI 和 CORBA 的結合產生了RMI-IIOP,RMI-IIOP 是企業(yè)服務器端 Java 開發(fā)的基礎。 1997 年,IBM 和 Sun Microsystems啟動了一項旨在促進 Java 作為企業(yè)開發(fā)技術的發(fā)展的合作計劃。兩家公司特別著力于如何將 Java 用作服務器端語言,生成可以結合進現(xiàn)有體系結構的企業(yè)級代碼。所需要的就是一種遠程傳輸技術,它兼有 Java 的 RMI(Remote Method Invocation,遠程方法調用)較少的資源占用量和更成熟的 CORBA(Common Object Request Broker Architecture,公共對象請求代理體系結構)技術的健壯性。出于這一需要,RMI-IIOP問世了,它幫助將 Java 語言推向了目前服務器端企業(yè)開發(fā)的主流語言的領先地位。 RMI示例 Java遠程方法調用(RMI)提供了Java程序語言的遠程通訊功能,這種特性使客戶機上運行的程序可以調用遠程服務器上的對象,使Java編程人員能夠在網(wǎng)絡環(huán)境中分布操作。 創(chuàng)建一個簡單的Java分布式遠程方法調用程序可以按以下幾個步驟操作, 一、定義遠程接口: 在 Java 中,遠程對象是實現(xiàn)遠程接口的類的實例, 遠程接口聲明每個要遠程調用的方法。在需要創(chuàng)建一個遠程對象的時候,我們通過傳遞一個接口來隱藏基層的實施細節(jié),客戶通過接口句柄發(fā)送消息即可。 遠程接口具有如下特點: 1) 遠程接口必須為public屬性。如果不這樣,除非客戶端與遠程接口在同一個包內,否則 當試圖裝入實現(xiàn)該遠程接口的遠程對象時,調用會得到錯誤結果。 2) 遠程接口必須擴展接口java.rmi.Remote。 3) 除與應用程序本身特定的例外之外,遠程接口中的每個方法都必須在自己的throws從句中 聲明java.rmi.RemoteException。(或RemoteException 的父類)。 4) 作為參數(shù)或返回值傳遞的一個遠程對象(不管是直接,還是本地對象中嵌入)必須聲明為遠 程接口,而不應聲明為實施類。 下面是遠程接口的接口RmiSample的定義 Java代碼 import java.rmi.*; public interface RmiSample extends Remote { public int sum(int a,int b) throws RemoteException; } 二、實現(xiàn)遠程接口: 遠程對象實現(xiàn)類必須擴展遠程對象java.rmi.UnicastRemoteObject類,并實現(xiàn)所定義的遠程接口。遠程對象的實現(xiàn)類中包含實現(xiàn)每個遠程接口所指定的遠程方法的代碼。這個類也可以含有附加的方法,但客戶只能使用遠程接口中的方法。因為客戶是指向接口的一個句柄,而不是它的哪個類。必須為遠程對象定義構造函數(shù),即使只準備定義一個默認構造函數(shù),用它調用基礎類構造函數(shù)。因為基礎類構造函數(shù)可能會拋出java.rmi.RemoteException,所以即使別無它用必須拋出java.rmi.RemoteException例外。 以下是遠程對象實現(xiàn)類的聲明: Java代碼 import java.rmi.*; importjava.rmi.server.*; public class RmiSampleImpl extends UnicastRemoteObject implements RmiSample { RmiSampleImpl() throws RemoteException { super(); } public int sum(int a,int b) throws RemoteException { return a + b; } } 三、編寫服務器類: 包含 main 方法的類可以是實現(xiàn)類自身,也可以完全是另一個類。下面通過RmiSampleServer 來創(chuàng)建一個遠程對象的實例,并通過java.rmi.registry.LocateRegistry類的createRegistry 方法從指定端口號啟動注冊服務程序,也可以通過執(zhí)行 rmiregistry 命令啟動注冊服務程序,注冊服務程序的缺省運行端口為 1099。必須將遠程對象名字綁定到對遠程對象的引用上:Naming.rebind("//localhost:8808/SAMPLE-SERVER" , Server); 以下是服務器類的聲明: Java代碼 import java.rmi.*; import java.rmi.registry.*; public class RmiSampleServer{ public static void main(String args[]) { try { LocateRegistry.createRegistry(8808) ; SampleServerImpl Server = new SampleServerImpl(); // 將該對象實例與名稱“SAMPLE-SERVER”捆綁 Naming.rebind("//localhost:8808/SAMPLE-SERVER" , Server); } catch (MalformedURLException me) { System.out.println("Malformed URL: " + me.toString()); } catch (RemoteException re) { System.out.println("Remote exception: " + re.toString()); } } } 四、編寫使用遠程服務的客戶機類: 客戶機類的主要功能有兩個,一是通過Naming.lookup方法來構造注冊服務程序stub 程序實例,二是調用服務器遠程對象上的遠程方法。 以下是服務器類的聲明: Java代碼 import java.rmi.*; importjava.rmi.server.*; public class RmiSampleClient { public static void main(String[] args) { try { String url = "//localhost:8808/SAMPLE-SERVER"; RmiSample RmiObject = (RmiSample)Naming.lookup(url); System.out.println(" 1 + 2 = " + RmiObject.sum(1,2) ); } catch (RemoteException exc) { System.out.println("Error in lookup: " + exc.toString()); } catch (MalformedURLException exc) { System.out.println("Malformed URL: " + exc.toString()); } catch (java.rmi.NotBoundException exc) { System.out.println("NotBound: " + exc.toString()); } } } 五、編譯代碼: 要編譯 Java 源文件,請運行javac 命令: Java代碼 javac RmiSample.java RmiSampleImpl.java RmiSampleServer.java RmiSampleClient.java javac RmiSample.java RmiSampleImpl.java RmiSampleServer.java RmiSampleClient.java 六、為遠程對象實現(xiàn)創(chuàng)建根和干: 要創(chuàng)建存根程序和骨架文件,應以包含遠程對象實現(xiàn)的已編譯類包全名運行 rmic編譯器。 存根(Stub)是遠程對象在客戶端的代理,它將RMI調用傳遞給服務器端的骨架(Skeleton),后者負責將該調用傳遞給實際的遠程方法輸入如下: Java代碼 D:\RMI>rmic -d D:\RMI RmiSampleImpl D:\RMI>rmic -d D:\RMI RmiSampleImpl 執(zhí)行這個命令, 若rmic成功運行,RMI目錄下就會多出兩個新類: RmiSampleImpl_Stub.class RmiSampleImpl_Skel.class 它們分別對應的是存根(stub)和骨架(skeleton). 七、運行代碼: 運行服務端程序:在Windows下,輸入下列命令,在后臺啟動RmiSampleServer程序: Java代碼 D:\RMI>java RmiSampleServer D:\RMI>java RmiSampleServer 運行客戶端程序: Java代碼 D:\RMI>java RmiSampleClient D:\RMI>java RmiSampleClient 客戶端輸出: 1 + 2 = 3總結
- 上一篇: vscode 编辑器快捷键
- 下一篇: LINUX 内核代码备忘录