對JAVA RMI的認識

RMI的定義

RPC (Remote Procedure Call):遠程方法調用,用於一個進程調用另外一個進程中的過程,從而提供了過程的分佈能力。java

RMI(Remote Method Invocation):遠程方法調用,即在RPC的基礎上有向前邁進了一步,提供分佈式對象間的通信。容許運行在一個java 虛擬機的對象調用運行在另外一個java虛擬機上對象的方法。這兩個虛擬機能夠是運行在相同計算機上的不一樣進程中,也能夠是運行在網絡上的不一樣計算機中。程序員

RMI的全稱宗旨就是儘可能簡化遠程接口對象的調用。編程

RMI大大加強了java開發分佈式應用的能力,例如能夠將計算方法複雜的程序放在其餘的服務器上,主服務器只須要去調用,而真正的運算是在其餘服務器上進行,最後將運算結果返回給主服務器,這樣就減輕了主服務器的負擔,提升了效率(可是也有其餘的開銷)。bootstrap

RMI網絡模型

在設計初始階段,咱們真正想要的是這樣一種機制,客戶端程序員以常規方式進行方法調用,而無需操心將數據發送到網絡上或者解析響應之類的問題。因此纔有了以下的網絡模型:在客戶端爲遠程對象安裝一個代理。代理是位於客戶端虛擬機中的一個對象,它對於客戶端程序來講,就像是要訪問的遠程對象同樣。客戶端調用此代理時,只需進行常規的方法調用。而客戶端代理則負責使用網絡協議與服務器進行聯繫。服務器

 

如今的問題在於代理之間是如何進行通訊的?一般有三種方法:網絡

一、CORBA:經過對象請求代理架構,支持任何編程語言編寫的對象之間的方法調用。架構

二、SOAP編程語言

三、RMI:JAVA的遠程方法調用技術,支持java的分佈式對象之間的方法調用。分佈式

其中CORBA與SOAP都是徹底獨立於言語的,可使用C、C++、JAVA來編寫,而RMI只適用於JAVA。性能

RMI的工做原理

1、術語介紹

一、存根:當客戶端要調用遠程對象的一個方法時,實際上調用的是代理對象上的一個普通方法,咱們稱此代理對象爲存根(stub)。存根位於客戶端機器上,而非服務器上。

二、參數編組:存根會將遠程方法所需的參數打包成一組字節,對參數編碼的過程就稱爲參數編組。參數編組的目的是將參數轉換成適合在虛擬機之間進行傳遞的格式,在RMI協議中,對象是使用序列化機制進行編碼的。

2、編程模型

爲了介紹RMI的編程模型,我下面會編寫一個DEMO。遠程對象表示的是一個倉庫,而客戶端程序向倉庫詢問某個產品的價格。

一、接口定義

遠程對象的能力是由在客戶端和服務器之間共享的接口所表示的:

 

package rmi;

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

public interface Warehouse extends Remote
{
    double getPrice(String description) throws RemoteException;
}

 

遠程對象的接口必須擴展Remote接口,它位於java.rmi包中。接口中全部的方法必須聲明拋出RemoteException異常。這是由於遠程方法老是存在失敗的可能,因此java編程語言要求每一次遠程方法的調用都必須捕獲RemoteException,而且指明當調用不成功時應執行的相應處理操做。

二、接口的實現

package rmi;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;

public class WarehouseImpl extends UnicastRemoteObject implements Warehouse
{
    private static final long serialVersionUID = 1L;
    private Map<String,Double> prices;
    protected WarehouseImpl() throws RemoteException
    {
        prices = new HashMap<String,Double>();
        prices.put("mate7",3700.00);
        
    }
    public double getPrice(String description) throws RemoteException
    {
        Double price = prices.get(description);
        return price == null? 0 : price;
    }

}

你能夠看出這個類是遠程方法調用的目標,由於它擴展自UnicastRemoteObject,這個類的構造器使得它的對象可供遠程訪問。

三、RMI註冊表:經過JNDI發佈RMI服務

一、要訪問服務器上的一個遠程對象時,客戶端必須先獲得一個本地的存根對象,也就是客戶端機器上的代理對象。那麼問題來了,如何才能獲得這個存根呢?

二、爲此,JDK提供了自舉註冊服務(bootstrap registry service),服務器程序應該使用自舉註冊服務來註冊至少一個遠程對象。

三、而要註冊一個遠程對象,須要一個RMI URL和一個對實現對象的引用。

四、RMI 的URL以rmi:開頭,後接域名或IP地址(host),緊接着是端口號(port),最後是服務名(service)。

     如:rmi://regserver.mycompany.cmo:99/central_warehouse

     若是咱們是在本地發佈RMI服務,那麼host就是「localhost」,此外RMI默認的端口號是「1099」,固然咱們也能夠自行設置,只要不與其餘端口重複便可。              service其實是基於同一個host與port下惟一的服務名。

發佈RMI服務:

 1 package rmi;
 2 
 3 import java.net.MalformedURLException;
 4 import java.rmi.AlreadyBoundException;
 5 import java.rmi.Naming;
 6 import java.rmi.RemoteException;
 7 import java.rmi.registry.LocateRegistry;
 8 
 9 import javax.naming.NamingException;
10 
11 
12 public class WarehouseServer
13 {
14     public static void main(String[] args) throws RemoteException, NamingException, MalformedURLException, AlreadyBoundException
15     {
16         System.out.println("Constructing server implementation");
17         WarehouseImpl centralWarehouse = new WarehouseImpl();
18         
19         System.out.println("Binding server implementation to registry");
20         LocateRegistry.createRegistry(1099);
21         Naming.bind("rmi://localhost:1099/central_warehoues",centralWarehouse);
22         
23         System.out.println("Waiting for invocations from clients ...");
24     }
25 }

運行結果:

Constructing server implementation
Binding server implementation to registry
Waiting for invocations from clients ...

一、第20行只需提供一個port,就在JNDI中建立了一個註冊表。

二、第21行經過bind方法綁定了RMI地址與RMI服務實現類。

三、執行這個方法後,至關於自動發佈了RMI服務。接下來要作的事情就是寫一個RM客戶端調用已發佈的RMI服務。

 四、調用RMI服務

 1 package rmi;
 2 
 3 import java.net.MalformedURLException;
 4 import java.rmi.Naming;
 5 import java.rmi.NotBoundException;
 6 import java.rmi.RemoteException;
 7 import javax.naming.NamingException;
 8 
 9 public class WarehouseClient
10 {
11     public static void main(String[] args) throws NamingException, RemoteException, MalformedURLException, NotBoundException
12     {
13         System.out.println("RMI registry binding:");
14         String url = "rmi://localhost:1099/central_warehoues";
15         Warehouse centralWarehouse = (Warehouse) Naming.lookup(url);
16         String descr = "mate7";
17         double price = centralWarehouse.getPrice(descr);
18         System.out.println(descr + ":" + price);
19     }
20 }

運行結果:

RMI registry binding:
mate7:3700.0

一、服務調用只須要知道兩個東西:一、RMI請求路徑;二、RMI接口名

二、第15行,這裏用的是接口名Warehouse,而不是實現類。必定不能RMI接口的實現類,不然就是本地調用了。

三、查看運行結果,咱們知道此次DEMO展現的遠程調用成功了。

五、下面咱們來看下RMI的網絡示意圖:

 

一、藉助JNDI這個所謂的命名與目錄服務,咱們成功地發佈並調用了RMI服務。實際上,JNDI就是一個註冊表,服務端將服務對象放入到註冊表中,客戶端從註冊表中獲取服務對象。

二、在服務端咱們發佈了RMI服務,並在JNDI中進行了註冊,此時就在服務端建立了一個Skeleton(骨架),當客戶端第一次成功鏈接JNDI並獲取遠程服務對象後,立馬在本地建立了一個Stub(存根)。

三、遠程通訊實際是經過Skeleton與Stub來完成的,數據是基於TCP/IP協議,在「傳輸層」上發送的。

四、毋庸置疑,理論上RMI必定比WebService要快,畢竟WebService是基於http協議的,而http所攜帶的數據是經過「應用層」來傳輸的。傳輸層較應用層更爲底層,越底層越快。

RMI的侷限性

一、只能實現JAVA系統之間的調用,而WebService能夠實現跨語言實現系統之間的調用。

二、RMI使用了JAVA默認的序列化方式,對於性能要求比較高的系統,可能須要其餘的序列化方案來解決。

三、RMI服務在運行時不免會存在故障,例如,若是RMI服務沒法鏈接了,就會致使客戶端沒法響應的現象。

相關文章
相關標籤/搜索