遠程方法調用(RMI)原理與示例

RMI介紹java

  遠程方法調用(RMI)顧名思義是一臺機器上的程序調用另外一臺機器上的方法。這樣能夠大體知道RMI是用來幹什麼的,可是這種理解還不太確切。RMI是Java支撐分佈式系統的基石,例如著名的EJB組件。RMI是遠程過程調用(RPC)的一種面向對象實現,RMI底層是經過socket通訊和對象序列化技術來實現的。這裏引用Wikipedia對RMI的介紹:服務器

The Java Remote Method Invocation (Java RMI) is a Java API that performs remote method invocation, the object-oriented equivalent of remote procedure calls (RPC), with support for direct transfer of serialized Java classes and distributed garbage collection.框架

  1. The original implementation depends on Java Virtual Machine(JVM) class representation mechanisms and it thus only supports making calls from one JVM to another. The protocol underlying this Java-only implementation is known as Java Remote Method Protocol (JRMP).
  2. In order to support code running in a non-JVM context, a CORBA version was later developed.

Usage of the term RMI may denote solely the programming interface or may signify both the API and JRMP, IIOP, or another implementation, whereas the term RMI-IIOP (read: RMI over IIOP) specifically denotes the RMI interface delegating most of the functionality to the supporting CORBA implementation.
The basic idea of Java RMI, the distributed garbage-collection (DGC) protocol, and much of the architecture underlying the original Sun implementation, come from the 'network objects' feature of Modula-3.socket

RMI基本原理分佈式

  RMI的目的就是要使運行在不一樣的計算機中的對象之間的調用表現得像本地調用同樣。RMI 應用程序一般包括兩個獨立的程序:服務器程序和客戶機程序。RMI 須要將行爲的定義與行爲的實現分別定義, 並容許將行爲定義代碼與行爲實現代碼存放並運行在不一樣的 JVM 上。在 RMI 中, 遠程服務的定義是存放在繼承了 Remote 的接口中。遠程服務的實現代碼存放在實現該定義接口的類中。RMI 支持兩個類實現一個相同的遠程服務接口: 一個類實現行爲並運行在服務器上, 而另外一個類做爲一個遠程服務的代理運行在客戶機上。客戶程序發出關於代理對象的調用方法, RMI 將該調用請求發送到遠程 JVM 上, 而且進一步發送到實現的方法中。實現方法將結果發送給代理, 再經過代理將結果返回給調用者。ide

  RMI 構建三個抽象層, 高層覆蓋低層, 分別負責Socket通訊, 參數和結果的序列化和反序列化等工做。存根( Stub) 和骨架( Skeleton) 合在一塊兒造成了 RMI 構架協議。下面的引用層被用來尋找各自的通訊夥伴, 在這一層還有一個提供名字服務的部分, 稱爲註冊表( registry) 。最下一層是傳輸層, 是依賴於 TCP/IP 協議實現客戶機與服務器的互聯。
  

  當客戶端調用遠程對象方法時, 存根負責把要調用的遠程對象方法的方法名及其參數編組打包,並將該包向下經遠程引用層、傳輸層轉發給遠程對象所在的服務器。經過 RMI 系統的 RMI 註冊表實現的簡單服務器名字服務, 可定位遠程對象所在的服務器。該包到達服務器後, 向上經遠程引用層, 被遠程對象的 Skeleton 接收, 此 Skeleton 解析客戶包中的方法名及編組的參數後, 在服務器端執行客戶要調用的遠程對象方法, 而後將該方法的返回值( 或產生的異常) 打包後經過相反路線返回給客戶端, 客戶端的 Stub 將返回結果解析後傳遞給客戶程序。事實上, 不只客戶端程序能夠經過存根調用服務器端的遠程對象的方法, 而服務器端的程序亦可經過由客戶端傳遞的遠程接口回調客戶端的遠程對象方法。在分佈式系統中, 全部的計算機能夠是服務器, 同時又能夠是客戶機。測試

 

RMI應用示例ui

  Remote 接口用於標識其方法能夠從非本地虛擬機上調用的接口。任何遠程對象都必須直接或間接實現此接口。只有在「遠程接口」(擴展 java.rmi.Remote 的接口)中指定的這些方法纔可遠程使用。 也就是說須要遠程調用的方法必須在擴展Remote接口的接口中聲名而且要拋出RemoteException異常才能被遠程調用。遠程對象必須實現java.rmi.server.UniCastRemoteObject類,這樣才能保證客戶端訪問得到遠程對象時,該遠程對象將會把自身的一個拷貝序列化後以Socket的形式傳輸給客戶端,此時客戶端所得到的這個拷貝稱爲「存根」,而服務器端自己已存在的遠程對象則稱之爲「骨架」。其實此時的存根是客戶端的一個代理,用於與服務器端的通訊,而骨架也可認爲是服務器端的一個代理,用於接收客戶端的請求以後調用遠程方法來響應客戶端的請求。 遠程對象的接口和實現必須在客戶端和服務器端同時存在而且保持一致才行。this

 

實現代碼:idea

package com.wxisme.rmi;

import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 *@Description:<p>遠程接口定義</p>
 *@author 王旭
 *@time 2016年3月14日 下午4:53:48
 */
public interface HelloDefine extends Remote {
    
    public String helloWorld() throws RemoteException;
    
    public String sayHello(String name) throws RemoteException;
    
}
package com.wxisme.rmi;

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

/**
 *@Description:<p>遠程接口實現</p>
 *@author 王旭
 *@time 2016年3月14日 下午4:57:50
 */
public class HelloDefineImp extends UnicastRemoteObject implements HelloDefine {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public HelloDefineImp() throws RemoteException {
        super();
    }

    public String helloWorld() throws RemoteException {
        return "Hello AlphaGo!";
    }

    public String sayHello(String name) throws RemoteException {
        return "Hello" + name +"!";
    }

}
package com.wxisme.rmi;

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

/**
 *@Description:<p>服務端綁定</p>
 *@author 王旭
 *@time 2016年3月14日 下午4:59:33
 */
public class HelloServer {
    
    HelloDefine hello;
    
    public void server() throws RemoteException, MalformedURLException, AlreadyBoundException {
        hello = new HelloDefineImp();
        
        //遠程對象註冊表實例
        LocateRegistry.createRegistry(8888);
        //把遠程對象註冊到RMI註冊服務器上
        Naming.bind("rmi://localhost:8888/Hello", hello);
        System.out.println("server:對象綁定成功!");
    }

}
package com.wxisme.rmi;

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

/**
 *@Description:<p>客戶端調用</p>
 *@author 王旭
 *@time 2016年3月14日 下午5:08:51
 */public class HelloClient {
    
    public HelloDefine hello;
    
    public void client() throws MalformedURLException, RemoteException, NotBoundException {
        //在RMI註冊表中查找指定對象
        hello = (HelloDefine) Naming.lookup("rmi://localhost:8888/Hello");
        //調用遠程對象方法
        System.out.println("client:");
        System.out.println(hello.helloWorld());
        System.out.println(hello.sayHello("神之一手"));
    }

}
package com.wxisme.rmi;

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

import org.junit.Test;

/**
 *@Description:<p>測試</p>
 *@author 王旭
 *@time 2016年3月14日 下午5:14:36
 */
public class RMITest {

    @Test
    public void testServer() throws RemoteException, MalformedURLException, AlreadyBoundException {
        HelloServer server = new HelloServer();
        server.server();
        while(true);
    }
    
    @Test
    public void testClient() throws MalformedURLException, RemoteException, NotBoundException {
        HelloClient client = new HelloClient();
        client.client();
    }

}

運行結果:

 

 

 

 

 

 

 

 

 

 

 

參考資料:

Wikipedia RMI:https://en.wikipedia.org/wiki/Java_remote_method_invocation

Java RMI 框架:http://haolloyin.blog.51cto.com/1177454/332426/

《基於 RMI 的文件上傳與下載的實現》程曉錦, 徐秀花

相關文章
相關標籤/搜索