Java RMI

Java RMI 指的是遠程方法調用 (Remote Method Invocation)。它是一種機制,可以讓在某個 Java 虛擬機上的對象調用另外一個 Java 虛擬機中的對象上的方法。能夠用此方法調用的任何對象必須實現該遠程接口。java

 
Java RMI不是什麼新技術(在Java1.1的時代都有了),但倒是是很是重要的底層技術。
大名鼎鼎的EJB都是創建在rmi基礎之上的,如今還有一些開源的遠程調用組件,其底層技術也是rmi。
 
在大力鼓吹Web Service、SOA的時代,是否是每一個應用都應該選用笨拙的Web Service組件來實現,經過對比測試後,RMI是最簡單的,在一些小的應用中是最合適的。
 
RMI對服務器的IP地址和端口依賴很緊密,可是在開發的時候不知道未來的服務器IP和端口如何,可是客戶端程序依賴這個IP和端口。
這也是RMI的侷限性之一。這個問題有兩種解決途徑:一是經過DNS來解決,二是經過封裝將IP暴露到程序代碼以外。
RMI的侷限性之二是RMI是Java語言的遠程調用,兩端的程序語言必須是Java實現,對於不一樣語言間的通信能夠考慮用Web Service或者公用對象請求代理體系(CORBA)來實現。
 

如今的問題在於代理之間是如何進行通訊的?一般有三種方法:程序員

一、CORBA:經過對象請求代理架構,支持任何編程語言編寫的對象之間的方法調用。數據庫

二、SOAP編程

三、RMI:JAVA的遠程方法調用技術,支持java的分佈式對象之間的方法調用。安全

其中CORBA與SOAP都是徹底獨立於言語的,可使用C、C++、JAVA來編寫,而RMI只適用於JAVA。服務器

 

 

 

相關概述

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),到達 服務器對象。 佔位程序扮演着遠程服務器對象的代理的角色,使該對象可被客戶激活。 遠程引用層處理語義、管理單一或多重對象的通訊,決定調用是應發往一個服務器仍是多個。傳輸層管理實際的鏈接,而且追追蹤能夠接受方法調用的遠程對象。服務器端的骨幹網完成對服務器對象實際的方法調用,並獲取返回值。返回值向下經遠程引用層、服務器端的 傳輸層傳遞迴客戶端,再向上經傳輸層和遠程調用層返回。最後,佔位程序得到返回值。
要完成以上步驟須要有如下幾個步驟:
一、生成一個遠程 接口
二、實現遠程對象( 服務器端程序)
三、生成佔位程序和骨幹網(服務器端程序)
四、編寫服務器程序
五、編寫客戶程序
六、註冊遠程對象
七、啓動遠程對象
具體實現以下:
一、生成一個遠程接口
package c15.ptime;
importjava.rmi.*;
public interface PerfectTimeI extends Remote {
long getPerfectTime() throws RemoteException;
}
二、實現遠程對象( 服務器端程序)
package c15.ptime;
import java.rmi.*;
import java.rmi.server.*;
import java.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();
}
}
}
四、編譯遠程對象(服務器端程序)
javac -classpath . -d . PerfectTime.java
五、生成根和幹(佔位程序和骨幹程序)
rmic -classpath . -d . c15.ptime.PerfectTime
注:jdk1.2之後的都不須要skeleton,因此若是你用的jdk爲5.0版本的,
不要奇怪爲何只產生了stub沒有skeleton。
六、註冊遠程對象
start rmiregistry 2005
注:綁定服務的默認端口爲1099,若是使用了這個端口,則能夠直接使用 start rmiregistry而不須要跟端口
若是這種註冊遠程對象的方法不起做用.
還有一種方法就是在綁定服務以前使用LocateRegistry.createRegistry(1099) 來註冊遠程對象.
七、啓動 服務器端程序
java-D java.rmi.server.codebase=file:///d:/TestRMI/ c15.ptime.Per
fectTime
八、編寫客戶端程序
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();
}
}
}
九、編譯客端程序
javac -classpath . -d . DisplayPerfectTime.java
十、修改JVM的配置文件 (客戶機和 服務器的都須要通過修改)
%JRE_HOME%\policytool.exe
十一、啓動客戶程序
java -classpath . c15.ptime.DisplayPerfectTime
十二、返回結果
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分佈式遠程方法調用程序能夠按如下幾個步驟操做,
1、定義遠程 接口
在 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; }
2、實現遠程 接口
遠程對象實現類必須擴展遠程對象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;
}
}
3、編寫 服務器類:
包含 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());
}
}
}
4、編寫使用 遠程服務的客戶機類:
客戶機類的主要功能有兩個,一是經過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());
}
}
}
5、編譯代碼:
要編譯 Java 源文件,請運行javac 命令:
Java代碼
javac RmiSample.java RmiSampleImpl.java RmiSampleServer.java RmiSampleClient.java
javac RmiSample.java RmiSampleImpl.java RmiSampleServer.java RmiSampleClient.java
6、爲遠程對象實現建立根和幹:
要建立存根程序和骨架文件,應以包含遠程對象實現的已編譯類包全名運行 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).
7、運行代碼:
運行服務端程序:在Windows下,輸入下列命令,在後臺啓動RmiSampleServer程序:
Java代碼
D:\RMI>java RmiSampleServer
D:\RMI>java RmiSampleServer
運行客戶端程序:
Java代碼
D:\RMI>java RmiSampleClient
D:\RMI>java RmiSampleClient
客戶端輸出: 1 + 2 = 3
RMI(Retailer Managed Inventory)零售商管理庫存 RMI(Retailer Managed Inventory,RMI),零售商管理庫存。是一種傳統的庫存管理方法,相對於VMI(Vendor Managed Inventory, 供應商管理庫存)而言,由零售商根據本身的銷售須要來控制管理庫存。
相關文章
相關標籤/搜索