一、什麼RMI
RMI即(Rmote Method Invoke)遠程方法調用。換句話說客戶對象Client可以調用遠程Server上的方法。客戶端並不是直接調用遠程服務上的方法,而是通過客戶輔助對象與遠程服務通信。客戶輔助對象會聯繫服務器,傳送相應的調用信息,等待服務器的返回。在服務器端,存在一個服務器輔助對象,該服務器輔助對象從客戶端輔助對象中接收請求(通過Socket連接),將請求交由真正的服務對象處理。最後服務器輔助對象將從服務中的得到的處理結果打包,然後發送給客戶輔助對象(通過Socket輸出流),客戶輔助對象將得到的結果解包,最後將返回結果交給客戶端。在RMI中將客戶端輔助對象稱爲存根Stub,將服務器輔助對象稱爲骨架Skeleton。
二、RMI通信流程
三、JAVA RMI 的一個簡單DEMO
製作一個可供遠程調用的對象,需要該類或者接口實現或者繼承java.rmi.Remote接口。該接口爲一個標記接口,其中不含有任何可供實現的方法。只用實現或集成該接口的類,才能成爲一個可供調用的遠程對象。
同時應該注意遠程方法的參數和返回值應該爲java基本類型或者Serializable類型。
爲了簡單起見一下源碼不採用包結構。
MyService接口源碼:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface MyService extends Remote { // 繼承Remote接口
// 定義能被遠程調用的方法
String service(String content) throws RemoteException;
}
實現該接口,MyServiceImpl源碼:
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
// 需要繼承UnicastRemoteObject類,用於導出的遠程對象和獲得與該遠程對象通信的存根
public class MyServiceImpl extends UnicastRemoteObject implements MyService {
private static final long serialVersionUID = -3145731787217567264L;
protected MyServiceImpl() throws RemoteException {
super();
}
// 實現服務方法
public String service(String content) {
return "server >> " + content;
}
}
Server類源碼:
import java.rmi.Naming;
public class Server {
public static void main(String[] args) {
try {
// 實例化服務類
MyService service = new MyServiceImpl();
// 將該服務註冊到RMI Registry中,服務名爲service
// 當綁定服務對象時,RMI會把服務換成Stub,然後把Stub放入registry中
Naming.rebind("service", service);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("register");
}
}
Client類源碼:
import java.rmi.Naming;
public class Client {
public static void main(String[] args) {
// service爲服務端註冊的服務名
String url = "rmi://localhost/service";
try {
// 客戶端到RMI Registry中查找服務
MyService service = (MyService) Naming.lookup(url);
Class stubClass = service.getClass();
System.out.println(stubClass + " 是 " + stubClass.getName() + "的實例");
Class[] infaces = stubClass.getInterfaces();
for(Class c : infaces) {
System.out.println("stub實現了" + c.getName() + "接口");
}
// 調用服務的方法並打印返回的結果
System.out.println(service.service(args[0]));
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意,爲了簡單起見所有源碼放在一個文件夾下
1.編譯源碼生成.class文件
// 指定編碼爲UTF-8
javac -encoding utf-8 *.java
2.通過rmic生成stub和skeleton,rmic爲JDK內置工具,通過該工具生成後綴爲_Stub和_Skel的文件(實際並未產生後綴爲_Skel的骨架文件,經過查詢發現Jdk1.2後不需要生成該文件)
// 注意是通過實現類生成,而不是接口
rmic MyServiceImpl
3.開啓另一個命令行窗口,執行rmiregistry注意該命令行窗口必須在源碼所在目錄打開(很重要),不然會報錯
4.啓動服務(啓動服務前必須保證rmiregistry運行)
java Server
5.運行客戶端代碼
java Client Hello,world!
四、後記
將生成的_Stub後綴文件刪除,重新運行Server和Client發現不一樣的運行結果。似乎生成了代理類(不是很清楚,待了解!)
參考:
http://haolloyin.blog.51cto.com/1177454/332426/
https://segmentfault.com/a/1190000002737588
《Head First設計模式》