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