參考:https://blog.csdn.net/kingcat666/article/details/78578578
一、RMI
RMI(Remote Method Invocation,遠程方法調用)。通過RMI技術,某一個本地的JVM可以調用存在於另外一個JVM中的對象方法,就好像它僅僅是在調用本地JVM中某個對象方法一樣。
二、RMI的適用場景
RMI是基於JAVA語言的,也就是說在RMI技術框架的描述中,只有Server端使用的是JAVA語言並且Client端也是用的JAVA語言,才能使用RMI技術RMI適用於兩個系統都主要使用JAVA語言進行構造,不需要考慮跨語言支持的情況。並且對兩個JAVA系統的通訊速度有要求的情況。
RMI 是一個良好的、特殊的RPC實現:使用JRMP協議承載數據描述,可以使用BIO和NIO兩種IO通信模型。RMI框架是可以在大規模集群系統中使用的。
三、RMI的基本框架
從設計角度上講,JAVA采用的是三層結構模式來實現RMI。在整個體系結構中,有如下幾個關鍵角色構成了通信雙方:
1.客戶端:
1)樁(StubObject):遠程對象在客戶端上的代理;
2)遠程引用層(RemoteReference Layer):解析並執行遠程引用協議;
3)傳輸層(Transport):發送調用、傳遞遠程方法參數、接收遠程方法執行結果。
2.服務端:
1)骨架(Skeleton):讀取客戶端傳遞的方法參數,調用服務器方的實際對象方法,並接收方法執行后的返回值;
2)遠程引用層(Remote ReferenceLayer):處理遠程引用語法之后向骨架發送遠程方法調用;
3)傳輸層(Transport):監聽客戶端的入站連接,接收並轉發調用到遠程引用層。
3.注冊表(Registry):以URL形式注冊遠程對象,並向客戶端回復對遠程對象的引用。
在實際的應用中,客戶端並沒有真正的和服務端直接對話來進行遠程調用,而是通過本地JVM環境下的樁對象來進行的。
遠程調用過程:
1)客戶端從遠程服務器的注冊表中查詢並獲取遠程對象引用。當進行遠程調用時,客戶端首先會與樁對象(Stub Object)進行對話,而這個樁對象將遠程方法所需的參數進行序列化后,傳遞給它下層的遠程引用層(RRL);
2)樁對象與遠程對象具有相同的接口和方法列表,當客戶端調用遠程對象時,實際上是由相應的樁對象代理完成的。遠程引用層在將樁的本地引用轉換為服務器上對象的遠程引用后,再將調用傳遞給傳輸層(Transport),由傳輸層通過TCP協議發送調用;
3)在服務器端,傳輸層監聽入站連接,它一旦接收到客戶端遠程調用后,就將這個引用轉發給其上層的遠程引用層;
4)服務器端的遠程引用層將客戶端發送的遠程應用轉換為本地虛擬機的引用后,再將請求傳遞給骨架(Skeleton);
5)骨架讀取參數,又將請求傳遞給服務器,最后由服務器進行實際的方法調用。
結果返回過程:
1)如果遠程方法調用后有返回值,則服務器將這些結果又沿着“骨架->遠程引用層->傳輸層”向下傳遞;
2)客戶端的傳輸層接收到返回值后,又沿着“傳輸層->遠程引用層->樁”向上傳遞,然后由樁來反序列化這些返回值,並將最終的結果傳遞給客戶端程序。
從技術的角度上講,有如下幾個主要類或接口扮演着上述三層模型中的關鍵角色:
1)注冊表:java.rmi.Naming和java.rmi.Registry;
2)骨架:java.rmi.remote.Skeleton
3)樁:java.rmi.server.RemoteStub
4)遠程引用層:java.rmi.server.RemoteRef和sun.rmi.transport.Endpoint;
5)傳輸層:sun.rmi.transport.Transport
四、RMI的開發流程
作為一般的RMI應用,JAVA為我們隱藏了其中的處理細節,而讓開發者有更多的精力和時間花在實際的應用中。開發RMI的步驟如下所述:
1.服務端:
1)定義Remote子接口,在其內部定義要發布的遠程方法,並且這些方法都要Throws RemoteException;
2)定義遠程對象的實現類,通常有兩種方式:
a. 繼承UnicastRemoteObject或Activatable,並同時實現Remote子接口;
b. 只實現Remote子接口和java.io.Serializable接口。
3)編譯樁(在JAVA 1.5及以后版本中,如果遠程對象實現類繼承了UnicastRemoteObject或Activatable,則無需此步,由JVM自動完成。否則需手工利用RMIC工具編譯生成此實現類對應的樁類,並放到和實現類相同的編譯目錄下);
4)啟動服務器:依次完成注冊表的啟動和遠程對象綁定。另外,如果遠程對象實現類在定義時沒有繼承UnicastRemoteObject或Activatable,則必須在服務器端顯式的調用UnicastRemoteObject類中某個重載的exportObject(Remote remote)靜態方法,將此實現類對象導出成為一個真正的遠程對象。
2.客戶端:
1)定義用於接收遠程對象的Remote子接口,只需實現java.rmi.Remote接口即可。但要求必須與服務器端對等的Remote子接口保持一致,即有相同的接口名稱、包路徑和方法列表等。
2)通過符合JRMP規范的URL字符串在注冊表中獲取並強轉成Remote子接口對象;
3)調用這個Remote子接口對象中的某個方法就是為一次遠程方法調用行為。