天天看點

RMI 說明

見:https://baike.baidu.com/item/RMI/1786244?fr=aladdin

RMI遠端方法調用

相關概述

RMI是Java的一組擁護開發 分布式應用程式的 API。RMI使用Java語言 接口定義了遠端對象,它集合了Java序列化和Java遠端方法協定(Java Remote Method Protocol)。簡單地說,這樣使原先的程式在同一作業系統的方法調用,變成了不同作業系統之間程式的方法調用,由于J2EE是分布式程式平台,它以RMI機制實作程式元件在不同作業系統之間的通信。比如,一個EJB可以通過RMI調用Web上另一台機器上的EJB遠端方法。 RMI(Remote Method Invocation,遠端方法調用)是用Java在JDK1.1中實作的,它大大增強了Java開發 分布式應用的能力。Java作為一種風靡一時的網絡開發語言,其巨大的威力就展現在它強大的開發分布式網絡應用的能力上,而RMI就是開發百分之百純Java的網絡分布式應用系統的核心解決方案之一。其實它可以被看作是RPC的Java版本。但是傳統RPC并不能很好地應用于 分布式對象系統。而Java RMI 則支援存儲于不同 位址空間的程式級對象之間彼此進行通信,實作遠端對象之間的無縫遠端調用。 RMI目前使用Java遠端消息交換協定JRMP(Java Remote Messaging Protocol)進行通信。JRMP是專為Java的遠端對象制定的協定。是以,Java RMI具有Java的“Write Once,Run Anywhere”的優點,是 分布式應用系統的百分之百純Java解決方案。用Java RMI開發的應用系統可以部署在任何支援JRE(Java Run Environment Java,運作環境)的平台上。但由于JRMP是專為Java對象制定的,是以,RMI對于用非Java語言開發的應用系統的支援不足。不能與用非Java語言書寫的對象進行通信。 Java Remote Method Invocation ( RMI -- Java遠端方法調用)允許您使用Java編寫 分布式對象。本文将介紹RMI的優點以及如何将其連接配接到現有的和原有的系統中,以及與用Java 編寫的元件的連接配接。 RMI為采用Java對象的 分布式計算提供了簡單而直接的途徑。這些對象可以是新的Java對象,也可以是圍繞現有API的簡單的Java包裝程式。Java展現了“編寫一次就能在任何地方運作的模式。而RMI可将Java模式進行擴充,使之可在任何地方運作”。 因為RMI是以Java為核心的,是以,它将Java的安全性和可移植性等強大功能帶給了分布式計算。您可将代理和業務邏輯等屬性移動到網絡中最合适的地方。如果您要擴充Java在系統中的使用,RMI将使您充分利用其強大功能。 RMI可利用标準Java本機方法 接口JNI [1]   與現有的和原有的系統相連接配接。RMI還可利用标準JDBC包與現有的關系資料庫連接配接。RMI/JNI和RMI/JDBC相結合,可幫助您利用RMI與目前使用非Java語言的現有 伺服器進行通信,而且在您需要時可擴充Java在這些伺服器上的使用。RMI可幫助您在擴充使用時充分利用Java的強大功能。

RMI系統運作機理

RMI應用程式通常包括兩個獨立的程式: 伺服器程式和客戶機程式。典型的伺服器應用程式将建立多個遠端對象,使這些遠端對象能夠被引用,然後等待客戶機調用這些遠端對象的方法。而典型的客戶機程式則從伺服器中得到一個或多個遠端對象的引用,然後調用遠端對象的方法。RMI為伺服器和客戶機進行通信和資訊傳遞提供了一種機制。 在與遠端對象的通信過程中,RMI使用标準機制:stub和skeleton。遠端對象的stub擔當遠端對象的客戶本地代表或代理人角色。調用程式将調用本地stub的方法,而本地stub将負責執行對遠端對象的方法調用。在RMI中,遠端對象的stub與該遠端對象所實作的遠端 接口集相同。調用stub的方法時将執行下列操作: (1) 初始化與包含遠端對象的遠端 虛拟機的連接配接; (2) 對遠端虛拟機的參數進行編組(寫入并傳輸); (3) 等待方法調用結果; (4) 解編(讀取)傳回值或傳回的異常; (5) 将值傳回給調用程式。為了向調用程式展示比較簡單的調用機制,stub将參數的序列化和網絡級通信等細節隐藏了起來。在遠端虛拟機中,每個遠端對象都可以有相應的skeleton(在JDK1.2環境中無需使用skeleton)。Skeleton負責将調用配置設定給實際的遠端對象實作。它在接收方法調用時執行下列操作:(1) 解編(讀取)遠端方法的參數;(2) 調用實際遠端對象實作上的方法;(3) 将結果(傳回值或異常)編組(寫入并傳輸)給調用程式。stub和skeleton由rmic 編譯器生成。 利用RMI編寫 分布式對象應用程式需要完成以下工作:(1) 定位遠端對象。應用程式可使用兩種機制中的一種得到對遠端對象的引用。它既可用RMI的簡單命名工具rmiregistry來注冊它的遠端對象,也可以将遠端對象引用作為正常操作的一部分來進行傳遞和傳回。(2)與遠端對象通信。遠端對象間通信的細節由RMI處理,對于程式員來說,遠端通信看起來就像标準的Java方法調用。(3)給作為參數或傳回值傳遞的對象加載類 位元組碼。因為RMI允許調用程式将純Java對象傳給遠端對象,是以,RMI将提供必要的機制,既可以加載對象的代碼又可以傳輸對象的資料。在RMI 分布式應用程式運作時, 伺服器調用注冊服務程式以使名字與遠端對象相關聯。客戶機在伺服器上的注冊服務程式中用遠端對象的名字查找該遠端對象,然後調用它的方法。

系統組成

一個正常工作的RMI系統由下面幾個部分組成: ·遠端服務的 接口定義 ·遠端 服務接口的具體實作 ·樁(Stub)和架構(Skeleton)檔案 ·一個運作 遠端服務的 伺服器 ·一個RMI命名服務,它允許用戶端去發現這個遠端服務 ·類檔案的提供者(一個HTTP或者 FTP伺服器) ·一個需要這個遠端服務的用戶端程式

技術原理

RMI系統結構,在用戶端和 伺服器端都有幾層結構。 --------- ---------- | 客戶 | |伺服器| ---------- ---------- | | ------------- ---------- | 占位程式| | 骨幹網| -------------- ----------- | | ------------------------------------ | 遠 程 引 用 層 | ------------------------------------ | | ------------------------------------ | 傳 輸 層 | ------------------------------------ 方法調用從客戶對象經占位程式(Stub)、遠端引用層(Remote Reference Layer)和 傳輸層(Transport Layer)向下,傳遞給主機,然後再次經傳 輸層,向上穿過遠端調用層和骨幹網(Skeleton),到達 伺服器對象。 占位程式扮演着遠端伺服器對象的代理的角色,使該對象可被客戶激活。 遠端引用層處理語義、管理單一或多重對象的通信,決定調用是應發往一個伺服器還是多個。傳輸層管理實際的連接配接,并且追追蹤可以接受方法調用的遠端對象。伺服器端的骨幹網完成對伺服器對象實際的方法調用,并擷取傳回值。傳回值向下經遠端引用層、伺服器端的 傳輸層傳遞回用戶端,再向上經傳輸層和遠端調用層傳回。最後,占位程式獲得傳回值。 要完成以上步驟需要有以下幾個步驟: 1、生成一個遠端 接口 2、實作遠端對象( 伺服器端程式) 3、生成占位程式和骨幹網(伺服器端程式) 4、編寫伺服器程式 5、編寫客戶程式 6、注冊遠端對象 7、啟動遠端對象

具體實作如下: 1、生成一個遠端接口 package c15.ptime; importjava.rmi.*; public interface PerfectTimeI extends Remote { long getPerfectTime() throws RemoteException; } 2、實作遠端對象( 伺服器端程式) package c15.ptime; import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; import java. &nbsp;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-D java.rmi.server.codebase=file:///d:/TestRMI/ c15.ptime.Per fectTime 8、編寫用戶端程式 package c15.ptime; import java.rmi.*; import java.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的配置檔案 (客戶機和 伺服器的都需要經過修改) %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(遠端方法調用)的優點

從最基本的角度看,RMI是Java的 遠端過程調用(RPC)機制。與傳統的RPC系統相比,RMI具有若幹優點,因為它是Java 面向對象方法的一部分。傳統的RPC系統采用中性語言,是以是最普通的系統--它們不能提供所有可能的目标平台所具有的功能。 RMI以Java為核心,可與采用本機方法與現有系統相連接配接。這就是說,RMI可采用自然、直接和功能全面的方式為您提供 分布式計算技術,而這種技術可幫助您以不斷遞增和無縫的方式為整個系統添加Java功能。 RMI的主要優點如下: 面向對象:RMI可将完整的對象作為參數和傳回值進行傳遞,而不僅僅是預定義的資料類型。也就是說,您可以将類似Java 哈希表這樣的複雜類型作為一個參數進行傳遞。而在目前的RPC系統中,您隻能依靠客戶機将此類對象分解成基本資料類型,然後傳遞這些資料類型,最後在 伺服器端重新建立哈希表。RMI則不需額外的客戶程式代碼(将對象分解成基本資料類型),直接跨網傳遞對象。 可移動屬性:RMI可将屬性(類實作程式)從客戶機移動到伺服器,或者從伺服器移到客戶機。例如,您可以定義一個檢查雇員開支報告的 接口,以便察看雇員是否遵守了公司目前實行的政策。在開支報告建立後,客戶機就會從伺服器端獲得實作該接口的對象。如果政策發生變化,伺服器端就會開始傳回使用了新政策的該接口的另一個實作程式。您不必在使用者系統上安裝任何新的軟體就能在用戶端檢查限制條件--進而向使用者提供爍快的回報,并降低 伺服器的工作量。這樣就能具備最大的靈活性,因為政策改變時隻需要您編寫一個新的Java類,并将其在伺服器 主機上安裝一次即可。 設計方式:對象傳遞功能使您可以在分布式計算中充分利用 面向對象技術的強大功能,如二層和 三層結構系統。如果您能夠傳遞屬性,那麼您就可以在您的解決方案中使用 面向對象的設計方式。所有面向對象的設計方式無不依靠不同的屬性來發揮功能,如果不能傳遞完整的對象--包括實作和類型--就會失去設計方式上所提供的優點。 安 全:RMI使用Java内置的安全機制保證下載下傳執行程式時使用者系統的安全。RMI使用專門為保護系統免遭惡意 小應用程式侵害而設計的安全 管理程式,可保護您的系統和網絡免遭潛在的惡意下載下傳程式的破壞。在情況嚴重時, 伺服器可拒絕下載下傳任何執行程式。 便于編寫和使用:RMI使得Java 遠端服務程式和通路這些服務程式的Java客戶程式的編寫工作變得輕松、簡單。遠端 接口實際上就是Java接口。服務程式大約用三行指令宣布本身是服務程式,其它方面則與任何其它Java對象類似。這種簡單方法便于快速編寫完整的 分布式對象系統的服務程式,并快速地制做軟體的原型和早期版本,以便于進行測試和評估。因為RMI程式編寫簡單,是以維護也簡單。 可連接配接現有/原有的系統:RMI可通過Java的本機方法接口JNI與現有系統進行進行互動。利用RMI和JNI,您就能用Java語言編寫用戶端程式,還能使用現有的 伺服器端程式。在使用RMI/JNI與現有伺服器連接配接時,您可以有選擇地用Java重新編寫服務程式的任何部分,并使新的程式充分發揮Java的功能。類似地,RMI可利用JDBC、在不修改使用資料庫的現有非Java 源代碼的前提下與現有關系資料庫進行互動。 編寫一次,到處運作:RMI是Java“編寫一次,到處運作 ”方法的一部分。任何基于RMI的系統均可100%地移植到任何Java 虛拟機上,RMI/JDBC系統也不例外。如果使用RMI/JNI與現有系統進行互動工作,則采用JNI編寫的代碼可與任何Java虛拟機進行編譯、運作。 分布式垃圾收集:RMI采用其分布式垃圾收集功能收集不再被網絡中任何客戶程式所引用的遠端服務對象。與Java虛拟機内部的垃圾收集類似,分布式垃圾收集功能允許使用者根據自己的需要定義 伺服器對象,并且明确這些對象在不再被客戶機引用時會被删除。 并行計算:RMI采用多線程處理方法,可使您的伺服器利用這些Java線程更好地 并行處理用戶端的請求。Java分布式計算解決方案:RMI從JDK 1.1開始就是 Java平台的核心部分,是以,它存在于任何一台1.1 Java虛拟機中。所有RMI系統均采用相同的公開協定,是以,所有Java 系統均可直接互相對話,而不必事先對協定進行轉換。

RMI與CORBA的關系

RMI 和 CORBA 常被視為互相競争的技術,因為兩者都提供對遠端 分布式對象的透明通路。但這兩種技術實際上是互相補充的,一者的長處正好可以彌補另一者的短處。RMI 和 CORBA 的結合産生了 RMI-IIOP,RMI-IIOP 是企業 伺服器端 Java 開發的基礎。 1997 年,IBM 和 Sun Microsystems啟動了一項旨在促進 Java 作為企業開發技術的發展的合作計劃。兩家公司特别着力于如何将 Java 用作伺服器端語言,生成可以結合進現有體系結構的企業級代碼。所需要的就是一種遠端傳輸技術,它兼有 Java 的 RMI(Remote Method Invocation,遠端方法調用)較少的資源占用量和更成熟的 CORBA(Common Object Request Broker Architecture, 公共對象請求代理體系結構)技術的 健壯性。出于這一需要,RMI-IIOP問世了,它幫助将 Java 語言推向了目前 伺服器端企業開發的主流語言的領先地位。 RMI示例 Java遠端方法調用(RMI)提供了Java程式語言的遠端通訊功能,這種特性使客戶機上運作的程式可以調用遠端伺服器上的對象,使Java程式設計人員能夠在網絡環境中分布操作。 建立一個簡單的Java分布式遠端方法調用程式可以按以下幾個步驟操作, 一、定義遠端 接口: 在 Java 中,遠端對象是實作遠端接口的類的執行個體, 遠端接口聲明每個要遠端調用的方法。在需要建立一個遠端對象的時候,我們通過傳遞一個接口來隐藏基層的實施細節,客戶通過接口句柄發送消息即可。 遠端接口具有如下特點: 1) 遠端接口必須為public屬性。如果不這樣,除非用戶端與遠端接口在同一個包内,否則 當試圖裝入實作該遠端接口的遠端對象時,調用會得到錯誤結果。 2) 遠端接口必須擴充接口java.rmi.Remote。 3) 除與應用程式本身特定的例外之外,遠端 接口中的每個方法都必須在自己的throws從句中 聲明java.rmi.RemoteException。(或RemoteException 的父類)。 4) 作為參數或傳回值傳遞的一個遠端對象(不管是直接,還是本地對象中嵌入)必須聲明為遠 程接口,而不應聲明為實施類。 下面是遠端接口的接口RmiSample的定義 Java代碼 import java.rmi.*; public interface RmiSample extends Remote { public int sum(int a,int b) throws RemoteException; } 二、實作遠端 接口: 遠端對象實作類必須擴充遠端對象java.rmi.UnicastRemoteObject類,并實作所定義的遠端接口。遠端對象的實作類中包含實作每個遠端接口所指定的遠端方法的代碼。這個類也可以含有附加的方法,但客戶隻能使用遠端接口中的方法。因為客戶是指向接口的一個句柄,而不是它的哪個類。必須為遠端對象定義 構造函數,即使隻準備定義一個 預設構造函數,用它調用基礎類構造函數。因為基礎類構造函數可能會抛出java.rmi.RemoteException,是以即使别無它用必須抛出java.rmi.RemoteException例外。 以下是遠端對象實作類的聲明: Java代碼 import java.rmi.*; import java.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 方法的類可以是實作類自身,也可以完全是另一個類。下面通過RmiSampleServer 來建立一個遠端對象的執行個體,并通過 java.rmi.registry.LocateRegistry類的createRegistry 方法從指定 端口号啟動注冊服務程式,也可以通過執行 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.*; import java.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 六、為遠端對象實作建立根和幹: 要建立存根程式和骨架檔案,應以包含遠端對象實作的已編譯類包全名運作 rmic 編譯器。 存根(Stub)是遠端對象在用戶端的代理,它将RMI調用傳遞給 伺服器端的骨架(Skeleton),後者負責将該調用傳遞給實際的遠端方法輸入如下: Java代碼 D:\RMI>rmic -d D:\RMI RmiSampleImpl D:\RMI>rmic -d D:\RMI RmiSampleImpl 執行這個指令, 若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