初識Java RMI框架

一、什麼RMI


RMI即(Rmote Method Invoke)遠程方法調用。換句話說客戶對象Client可以調用遠程Server上的方法。客戶端並不是直接調用遠程服務上的方法,而是通過客戶輔助對象與遠程服務通信。客戶輔助對象會聯繫服務器,傳送相應的調用信息,等待服務器的返回。在服務器端,存在一個服務器輔助對象,該服務器輔助對象從客戶端輔助對象中接收請求(通過Socket連接),將請求交由真正的服務對象處理。最後服務器輔助對象將從服務中的得到的處理結果打包,然後發送給客戶輔助對象(通過Socket輸出流),客戶輔助對象將得到的結果解包,最後將返回結果交給客戶端。在RMI中將客戶端輔助對象稱爲存根Stub,將服務器輔助對象稱爲骨架Skeleton

二、RMI通信流程


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

生成Stub文件

這裏寫圖片描述

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設計模式》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章