這種機制給分布式計算的系統設計和編程帶來了極大的方便。只要程序是按照RMI規則設計的,就不用問RMI下的網絡細節,比如TCP和Socket。任何兩臺計算機之間的通信完全由RMI負責。調用遠程計算機上的對象和調用本地對象壹樣方便。?
1,面向對象:?
RMI可以傳遞完整的對象作為參數和返回值,而不僅僅是預定義的數據類型。換句話說,像Java哈希表這樣的復雜類型可以作為參數傳遞。
2.可移動屬性:?
RMI可以將屬性從客戶機移動到服務器,或者從服務器移動到客戶機。
3.設計方法:?
對象傳遞函數使您能夠充分利用面向對象技術在分布式計算中的強大功能,如兩層和三層結構系統。如果用戶可以傳遞屬性,他們就可以在自己的解決方案中使用面向對象的設計。所有面向對象的設計方法都依賴於不同的屬性來發揮其功能。如果不能交付完整的對象,包括實現和類型,設計方法提供的優勢將會喪失。
4.安全性:?
RMI使用Java內置的安全機制來保證用戶系統在下載和執行程序時的安全性。RMI使用專門設計的安全管理程序來保護系統免受惡意小程序的攻擊。
5.易寫易用?
RMI使得編寫Java遠程服務程序和訪問這些服務程序的Java客戶機程序變得容易和簡單。遠程接口實際上是壹個Java接口。為了實現RMI的功能,必須創建遠程對象,任何可以遠程調用的對象都必須實現遠程接口。但是遠程接口本身不包含任何方法。因此有必要創建壹個新的接口來擴展遠程接口。新接口將包含所有可以遠程調用的方法。遠程對象必須實現這個新接口。因為新接口擴展了遠程接口,實現了新接口,所以滿足了遠程對象實現遠程接口的要求,實現的每個對象都會作為遠程對象被引用。?
國外的壹個PPT也總結了:?
Java RMI優勢:?
完全對象支持?
跨平臺。能力?
強大的通信?
大型物體?
客戶端和服務器的安全性?
代碼的分發/更新
RMI的缺點
從上面的過程來看,RMI是緊密依賴於服務器的IP地址和端口的,但是我們在開發的時候並不知道服務器未來的IP和端口,但是客戶端程序依賴於這個IP和端口。這也是RMI的局限性之壹。解決這個問題有兩種方法:壹種是通過DNS解決,另壹種是通過封裝將IP暴露在程序代碼之外。
RMI的第二個限制是RMI是Java語言的遠程調用,兩端的編程語言必須用Java實現。對於不同語言之間的通信,可以考慮Web Service或CORBA。?
國外的壹個PPT也總結了:?
Java RMI缺點:?
Java RMI只支持Java?
單壹供應商的專有協議?
需要RMI查找?
需要非標準端口
RMI和Socket的比較
RMI技術比較socket網絡編程主要有以下幾個方面:
首先,RMI是面向對象的,而後者不是。?
其次,RMI是受語言限制的。比如使用Java RMI技術時,客戶端和服務器端都必須使用Java開發。socket的網絡編程獨立於開發語言甚至平臺。基於socket的網絡編程,客戶端和服務器端可以使用不同的開發語言和不同的平臺。?
再次,從網絡協議棧的角度來看,RMI和socket的網絡編程處於不同的層次。基於socket的網絡編程在TCP協議之上,而RMI定義了自己的應用協議,其傳輸層采用Java遠程方法協議(JRMP)。可以看出,基於RMI的應用在網絡協議棧中的地位更高,這也決定了相比socket的網絡編程,RMI會失去壹些靈活性和可控性,但好處是給應用開發者帶來更多的簡單、方便和易用性。例如,如果使用RMI,就不需要關心消息是如何序列化的,只需要像本地方法調用壹樣使用RMI。代價就是應用開發者不能很好的控制消息的序列化機制。?
第四,這是最後壹個區別,我認為也是更重要的壹點,就是兩種方法的性能比較,這往往決定了妳會使用哪種技術來開發妳的應用。?
實驗結果表明,與基於TCP的socket相比,RMI在傳輸相同的有效數據時需要占用更多的網絡帶寬(協議開銷)。由此可以得出壹個大概的結論:RMI主要用於遠程方法的“調用”(多麽真實的RMI:),其技術內涵強調“調用”。基於此,我可以想到:移動計算和遠程控制。當妳的應用不需要在客戶端和服務器之間傳輸大量數據時,RMI是更好的選擇,它簡單且易於開發。但是壹旦妳的應用需要在客戶端和服務器之間傳輸大量的數據,這種情況比較極端,比如FTP應用,RMI就不適合了,我們應該使用socket。
PS: RMI的效率還是很高的,壹般比Hessian效率高,比Web Service效率高很多。當然,和socket之類的東西比起來,效率稍微低壹點,socket稍微低壹點。RMI的具體實現仍然依賴於底層的套接字編程。
壹個簡單的RMI系統通常可以分為四個文件。在這裏,介紹每個文件的創建和功能。
步驟1:創建遠程對象接口。
導入Java . RMI . remote;
導入Java . RMI . remote exception;
/*
*該接口繼承自Remote,每個定義的方法必須拋出壹個RemoteException異常對象。
*我們可以遠程調用的方法通過這裏向公眾開放。
*/公共接口IRMI擴展遠程{
公共字符串invoke()引發RemoteException
} 12345678910
步驟2:創建接口的具體實現類。
導入Java . RMI . remote exception;
導入Java . RMI . server . unicastremoteobject;
/*
*遠程對象的實現
*這裏定義了public方法的具體實現。
*/公共類IRMIImpl擴展UnicastRemoteObject實現IRMI {
受保護的IRMIImpl()引發RemoteException {
super();//此實現必須有壹個顯式構造函數,並引發RemoteException異常。
}
private static final long serialVersionUID = 6131922116577454476 l;
公共字符串invoke()引發RemoteException {?//該方法公開了
回“妳好,世界!”;
}
公共字符串tryinvoke()拋出remote exception {//此方法不是公共的,如果要使其公共,請在接口中定義它。
返回“嘗試遠程控制我”;
}
} 1234567891011121314151617181920212223
步驟3:創建壹個RMI服務器
導入Java . RMI . naming;
導入Java . RMI . registry . locate registry;
導入Java . RMI . registry . registry;
/*
*遠程對象的註冊類。這個類應該在服務器端執行。執行後,
*機器將成為RMI服務器,客戶端可以通過正確的url訪問它。
*遠程服務器上的對象,執行外部暴露的方法。
*/公共類RMIServer {
靜態int端口= 8888;
/*
*創建壹個註冊表對象。
* LocateRegistry用於獲取名稱服務或創建名稱服務。
*調用LocateRegistry方法。Create Registry (Intport)在特定端口創建名稱服務,這樣用戶就不需要手動啟動rmiregistry。
* @return返回註冊表對象。
*/
私有靜態註冊表createRegistry() {
註冊表registry = null
嘗試{
registry = locate registry . get registry(port);//如果端口未註冊,則會引發異常。
registry . list();//獲取該端口註冊的rmi對象。
} catch(最終異常e) {
嘗試{
registry = locate registry . create registry(port);//捕捉異常,端口註冊
} catch(最終異常ee) {
ee . printstacktrace();
}
}
返回註冊表;
}
/**
*將對象註冊到rmi服務器。
*/
公共靜態void bind() {
註冊表registry =?create registry();
嘗試{
IRMIImpl impl = new IRMIImpl();
registry.rebind("mytask ",impl);//這是綁定。客戶端中的lookup必須與“mytast”相同,才能遠程調用impl。
} catch(異常e) {
e . printstacktrace();
}
}
公共靜態void main(String[] args) {
嘗試{
bind();
} catch(異常e) {
e . printstacktrace();
}
}
} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
以上是壹個很好的寫法。如果只是想測試,可以直接寫在main()方法中:
ImplementClass IC = new ImplementClass();//具體實現類
註冊表r = locate registry . create registry(8888);
r.bind("mytask ",IC);
//naming . rebind(" RMI://localhost:8888/my task ",IC);可替換的上部句子1234
1.註冊壹個端口。2.將taskName和ImplementationClass綁定到註冊的端口。3.客戶端可以通過url和taskName找到ImplementationClass。
步驟4:創建RMI客戶端
導入Java . RMI . naming;
公共類RMIClient {
/**
*調用遠程對象中的方法
* @拋出異常
*/
公共靜態void getRemoteObject()引發異常{
/*遠程發布服務
返回對與指定名稱關聯的遠程對象的引用(存根)*/
IRMI obj =(IRMI)naming . lookup(" RMI://localhost:"+RMI server . port+"/my task ");?//註:通過接口取。
system . out . println(obj . invoke());//調用遠程服務的方法
}
公共靜態void main(String[] args) {
嘗試{
getRemoteObject();
} catch(異常e) {
e . printstacktrace();
}
}
} 123456789101112131415161718192021222324
運行RMI系統:啟動RMI服務器並啟動客戶機。