RMI原理及开发实例
http://blog.sina.com.cn/s/blog_492dbb6b0100faot.html
一、RMI原理簡介
1、RMI定義和功能
RMI是Remote Method Invocation的簡稱,是J2SE的一部分,能夠讓程序員開發(fā)出基于Java的分布式應(yīng)用。一個RMI對象是一個遠 程Java對象,可以從另一個Java虛擬機上(甚至跨過網(wǎng)絡(luò))調(diào)用它的方法,可以像調(diào)用本地Java對象的方法一樣調(diào)用遠程對象的方法,使分布在不同的JVM中的對象的外表和行為都 像本地對象一樣。
2、Stub和Skeleton介紹
在學(xué)習(xí)RMI的時,我們不能不討論stub和skeleton作用和相關(guān)問題。他們是我們理解RMI原理的關(guān)鍵。我做個比方說明這兩個概念。假如你是A,你想借D的工具,但是又不認識D的管家C,所以你找來B來幫你,B認識C。B在這時就是一個代理,代理你的請求,依靠自己的話語去借。C呢他負責(zé)D家東西收回和借出 ,但是要有D的批準。在得到D的批準以后,C再把東西給B,B呢再轉(zhuǎn)給A。stub和skeleton在RMI中就是角色就是B和C,他們都是代理角色,在現(xiàn)實開發(fā)中隱藏系統(tǒng)和網(wǎng)絡(luò)的的差異,這一部分的功能在RMI開發(fā)中對程序員是透明的。Stub為客戶端編碼遠程命令并把他們發(fā)送到服務(wù)器。而Skeleton則是把遠程命令解碼,調(diào)用服務(wù)端的遠程對象的方法,把結(jié)果在編碼發(fā)給stub,然后stub再解碼返回調(diào)用結(jié)果給客戶端。
從JDK5.0以后,這兩個類就不需要rmic來產(chǎn)生了,而是有JVM自動處理,實際上他們還是存在的。Stub存在于客戶端,作為客戶端的代理,讓我們總是認為客戶端產(chǎn)生了stub,接口沒有作用。實際上stub類是通過Java動態(tài)類下載 機制下載的(具體內(nèi)容請參考:Java RMI實現(xiàn)代碼動態(tài)下載),它是由服 務(wù)端產(chǎn)生,然后根據(jù)需要動態(tài)的加載到客戶端,如果下次再運行這個客戶端該存根類存在于classpath中,它就不需要再下載了,而是直接加載。(具體的內(nèi)部細節(jié),需要參考Sun 的Rmi - Java Remote Method Invocation – Specification)。總的來說,stub是在服務(wù)端產(chǎn)生的,如果服務(wù)端的stub內(nèi)容改變,那么客戶端的也是需要同步更新。
3、Rmiregistry介紹
Rmiregistry需要在提供遠程對象服務(wù)端啟動,它提供了一個環(huán)境,說白了就是在內(nèi)存中,開辟了一片空間,用來接受服務(wù)端服務(wù)程序的注冊,產(chǎn)生一個類似于數(shù)據(jù)庫,提供存儲檢索遠程對象功能的注冊表。這個RMI注冊表,就是我們常聽到的RMI名字服務(wù)。遠程客戶端,就是依靠它獲得存根,調(diào)用遠程方法。說到 這里有一個端口的問題,如果你在啟動rmiregistry時,設(shè)定了非默認端口,那么需要在服務(wù)端和客戶端統(tǒng)一使用該端口,否則就會有RemoteException的異常拋出,rmiregistry提 供的服務(wù)是針對特定的端口號的,不然在同一臺機器上也是無法提供服務(wù)。
二、RMI實例開發(fā)
????在本例中我們實現(xiàn)一個求加法的遠程服務(wù),下面我們通過具體的例子,建立一個簡單的遠程計算服務(wù)和使用它的客戶程序:
一個正常工作的RMI系統(tǒng)由下面幾個部分組成:
● 遠程服務(wù)的接口定義
● 遠程服務(wù)接口的具體實現(xiàn)
● 樁(Stub)和框架(Skeleton)文件
● 一個運行遠程服務(wù)的服務(wù)器
● 一個RMI命名服務(wù),它答應(yīng)客戶端去發(fā)現(xiàn)這個遠程服務(wù)
● 類文件的提供者(一個HTTP或者FTP服務(wù)器)
● 一個需要這個遠程服務(wù)的客戶端程序
下面我們一步一步建立一個簡單的RMI系統(tǒng)。首先在你的機器里建立一個新的文件夾,以便放置我們創(chuàng)建的文件,為了簡單起見,我們只使用一個文件夾存放客戶端和服務(wù)端代碼,并且在同一個目錄下運行服務(wù)端和客戶端。
假如所有的RMI文件都已經(jīng)設(shè)計好了,那么你需要下面的幾個步驟去生成你的系統(tǒng):
1、編寫并且編譯接口的Java代碼
package lib.idc;
public interface Calculator extends Remote
{
????public long add(long a, long b)?
????????throws java.rmi.RemoteException;
}
用命令編譯Calculator.java
>>javac?lib/idc/Calculator.java
2、編寫并且編譯接口實現(xiàn)的Java代碼
package lib.idc;
import java.rmi.server.UnicastRemoteObject;
public class CalculatorImpl extends UnicastRemoteObject implements Calculator{?
//這個實現(xiàn)必須有一個顯式的構(gòu)造函數(shù),并且要拋出一個RemoteException異常
????public CalculatorImpl()?
????????throws java.rmi.RemoteException {?
????????super();?
????}?
?
????public long add(long a, long b) throws java.rmi.RemoteException {?
????????return a + b;?
}?
}
同樣按照以上步驟編譯CalculatorImpl.java
>>javac?lib/idc/ CalculatorImpl.java
3、從接口實現(xiàn)類中生成樁(Stub)和框架(Skeleton)類文件
>>rmic lib.idc.CalculatorImpl
在你的目錄下運行上面的命令,成功執(zhí)行完上面的命令你可以發(fā)現(xiàn)一個Calculator_stub.class文件,假如你是使用的Java2SDK,那么你還可以發(fā)現(xiàn)Calculator_Skel.class文件。(我的沒有)
4、編寫遠程服務(wù)的主運行程序
遠程RMI服務(wù)必須是在一個服務(wù)器中運行的。CalculatorServer類是一個非常簡單的服務(wù)器。
package lib.idc;
import java.rmi.Naming;
public class CalculatorServer {
public CalculatorServer() {
try{
??Calculator c = new CalculatorImpl();
??Naming.rebind("rmi://localhost:1099/CalculatorService", c);
}catch (Exception e) {
????System.out.println("Trouble: " + e);
}
}
public static void main(String args[]) {
new CalculatorServer();
}
}
建立這個服務(wù)器程序,然后保存到你的目錄下。
5、編寫RMI的客戶端程序
package lib.idc;
import java.rmi.Naming;?
import java.rmi.RemoteException;?
import java.net.MalformedURLException;?
import java.rmi.NotBoundException;?
import java.rmi.RMISecurityManager;
public class CalculatorClient {?
??public static void main(String[] args) {
????????if(System.getSecurityManager()==null)
??????????????System.setSecurityManager(new RMISecurityManager());
??
????????try {?
????????????Calculator c = (Calculator)
???????????????????????????Naming.lookup(
?????????????????"rmi://localhost/CalculatorService");?
????????????????System.out.println( c.add(4, 5) );
} catch (MalformedURLException murle) {?
??????????????System.out.println("MalformedURLException");?
????????????}?
???????????????catch (RemoteException re) {?
??????????????System.out.println("RemoteException");?
??????????}?
}
6、運行RMI系統(tǒng)
????完成以上步驟后,運行啟動服務(wù)注冊命令:
>>rmiregistry(注意所有命令的運行都是在lib/idc的上級目錄,即. /lib/idc)
此命令執(zhí)行后,將啟動注冊服務(wù)程序,然后打開新的控制臺窗口(如果在應(yīng)用程序中啟動服務(wù)端程序,也必須先生成樁和框架類文件和運行rmiregistry命令)。同樣進入目錄. /lib/idc,執(zhí)行啟動服務(wù)端程序。因為RMI的安全機制將在服務(wù)端發(fā)生作用,所以你必須增加一條安全策略:?
grant?{
permission?java.security.AllPermission?"",?"";
};并將其添加到\..\jre\lib\security\ java.policy文件之后,該文件位于指定的jre路徑下。
>> javac lib/idc/CalculatorServer.java
>> java??lib.idc. CalculatorServer
這一步完成后打開第三個控制臺窗口,進入目錄. /lib/idc,執(zhí)行
命令啟動客戶端程序
>> javac lib/idc/ CalculatorClient.java
>> java??lib.idc. CalculatorClient
至此完成RMI系統(tǒng)的運行。
三、常見錯誤
1、error unmarshalling return; nested exception is:
????????java.lang.ClassNotFoundException: rmi.RmiInterfaceImp_Stub (no security manager: RMI class loader disabled)
解決方法:在客戶端添加以上紅色部分代碼。
2、Exception in thread "main" java.security.AccessControlException: access denied (
java.net.SocketPermission 211.69.205.250:1099 connect,resolve)
解決方法:新建文件如policy.txt 內(nèi)容為:
grant?{
permission?java.security.AllPermission?"",?"";
};并保存在客戶端程序目錄下,然后執(zhí)行
>>java??-Djava.security.policy=policy.txt lib.idc.CalculatorClient
3、>>rmic RmiInterfaceImp
error: 未找到類 RmiInterfaceImp。1 個錯誤
解決方法:出現(xiàn)此問題一般是RmiInterfaceImp的路徑問題,如在此處改為lib.idc.snatch.RmiInterfaceImp,表示RmiInterfaceImp所在的包。
4、error unmarshalling return; nested exception is:
????????java.lang.ClassNotFoundException: lib.idc.snatch.RmiInterfaceImp_Stub
解決方法:出現(xiàn)此類問題的原因是找不到RmiInterfaceImp_Stub,但是在同樣的目錄下有時可以找到,有時又找不到了,真是活見鬼了!這個問題還沒徹底解決,如果哪位兄弟解決了,還請不忘相告一聲。
總結(jié)
以上是生活随笔為你收集整理的RMI原理及开发实例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java远程方法调用
- 下一篇: Erlang与java的内存架构比较