Java RMI(遠程方法調用) 實例與分析

目的:

  經過本文,能夠加深對Java RMI的理解,知道它的工做原理,怎麼使用等. 也爲了加深我本身的理解,故整理成文.不足之處,還望指出.java

概念解釋:

RMI(RemoteMethodInvocation):遠程方法調用,顧名思義,經過遠程的方式調用非本地對象的方法並返回結果。使用遠程調用一般解決本地計算瓶頸問題,例如分佈式記算,最近很火的阿爾法狗人機大戰,聽說運算使用上千個CPU。socket

JRMP(java remote method protocol):java遠程方法協議,這是完成java到java遠程調用的協議,基於TCP協議。分佈式

stub與skeleton:這兩個概念下面會用到,這裏解釋下,skeleton是放在服務端的代理,它知道真正的對象在哪。stub是放在客戶端的代理,它記錄了查找和調用skeleton信息。理解成遠程對象引用也成.工具

 

容易混淆的概念:測試

  遠程方法調用與遠程過程調用的區別:遠程方法調用是java獨有的,基於JRMP對象流協議實現,支持傳輸java序列化對象。遠程過程調用是基於socket技術實現的,不能傳輸java對象,socket套接字協議支持多種語言。它們都是基於TCP協議傳輸。遠程方法調用傳輸的是java序列化對象和基本數據類型,而遠程過程調用不支持傳輸對象。this

RMI調用模型:spa

從宏觀看,想要遠程調用須要作兩件事情,1,服務端向本地對象註冊表中註冊能被調用的遠程對象. 2,客戶端向遠程對象註冊表請求遠程對象的引用..net

Java中RMI實現:

 先經過一個例子瞭解Java中RMI是怎麼用的,而後再根據代碼分析源碼是如何實現的.代理

1,先建立遠程對象接口,繼承自Remote(稍後源碼中有分析爲何要有這個接口)code

package remote.test;

import java.rmi.Remote;
import java.rmi.RemoteException;
/**
 * 遠程接口,實現Remote
 * @author lxz
 *
 */
public interface IRemote extends Remote{

    public String show()throws RemoteException;//聲明方法
}

2,接口實現,須要繼承UnicastRemoteObject類,等會分析

package remote.test;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
 * 遠程接口實現,繼承UnicastRemoteObject
 * @author lxz
 *
 */
public class RemoteImpl extends UnicastRemoteObject  implements IRemote{
    public RemoteImpl()throws RemoteException{}//構造方法
    public String show()throws RemoteException{//調用方法實現
        System.out.println("進入");
        System.out.println(this.toString());
        return "遠程調用成功";
    }
}

3,服務端向本地端口1234對象註冊表註冊對象和它的名字

package remote.test;

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

/**
 *  服務端啓動,建立端口上的對象註冊列表,向對象註冊表中註冊遠程調用對象
 * @author lxz
 *
 */
public class TestServer {
    public static void main(String[] args) throws MalformedURLException, RemoteException, AlreadyBoundException, InterruptedException {
        RemoteImpl r = new RemoteImpl();//建立遠程對象
        Registry rr = LocateRegistry.createRegistry(1234); //建立1234端口上的對象註冊表,若是已經建立了就用getRegistry方法獲取
        rr.bind("testrmi", r);//向註冊表中註冊對象
        System.out.println(r.toString());
    }
}    

4,根據JDK API,以上遠程服務就算搭建完畢了,下面經過客戶端調用測試

package remote.test;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

/**
 * 客戶端啓動,得到遠程的對象註冊表中的對象引用
 * @author lxz
 *
 */
public class TestClient {

    public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
        IRemote r = (IRemote) Naming.lookup("rmi://localhost:1234/testrmi");//獲取遠程1234端口對象註冊表中testrmi的stub
        String a = r.show();//調用引用的方法,實際上調用的是stub,由stub與服務端交互並返回結果
        System.out.println(a);
    }
}


執行結果以下:

------------------------------------------

服務端:

RemoteImpl[UnicastServerRef [liveRef: [endpoint:[192.168.1.253:58169](local),objID:[-60651394:1539d5944e6:-7fff, -6910034932968554489]]]]
客戶端:

遠程調用成功
------------------------------------------

這樣就完成了一個遠程對象註冊與遠程對象方法調用的完整例子. 如今根據這個例子來分析它爲何要繼承UnicastRemoteObject,實現Remote,向註冊表註冊等等.

首先遠程對象實現類中須要繼承UnicastRemoteObject類,UnicastRemoteObject具備註冊爲遠程對象,生成遠程引用的功能等,全部都已經被JDK封裝好了,不須要編寫,其中的實現有些是sun包開頭的,不公開.

UnicastRemoteObject繼承關係:

 

有了遠程對象實現類,看服務端的啓動邏輯,其中:

Registry rr = LocateRegistry.createRegistry(1234); 

LocateRegistry類:用於建立或獲取某端口的對象註冊表

LocateRegistry.createRegistry:這個方法表示得到遠程對象註冊表引用,返回Registry對象

Registry:真正操做遠程對象註冊表的接口

接着,

rr.bind("testrmi", r);

利用Registry的對象,把剛剛建立的遠程對象註冊爲名稱testrmi. 這裏還有一種寫法,效果是同樣的.

LocateRegistry.createRegistry(1234); //建立,若是已經建立了就可省略這一句
Naming.bind("rmi://localhost:1234/testrmi", r);//須要帶上端口

Naming:與對象註冊表交互的工具類

上面是服務端從遠程對象建立到對象註冊的整個邏輯.客戶端調用的邏輯比較簡單,先經過Naming工具類獲取到遠程對象的引用之後,就能夠正常使用了

(IRemote) Naming.lookup("rmi://localhost:1234/testrmi");

這裏返回的"引用"和一般講的對象引用不一樣,是遠程對象的引用信息.拿到這個"引用"之後就能夠像使用真正的對象同樣調用其中的方法.

 

結束.

相關文章
相關標籤/搜索