Hadoop RPC詳解-RMI

Hadoop RPC 遠程過程調用是Hadoop中的核心概念。在深入研究RPC之前,先看看遠程調用的鼻祖Java RMI.


1.什麼是RMI

Java遠程方法調用,即Java RMI(Java Remote Method Invocation)是Java編程語言裏,一種用於實現遠程過程調用應用程序編程接口。它使客戶機上運行的程序可以調用遠程服務器上的對象。遠程方法調用特性使Java編程人員能夠在網絡環境中分佈操作。RMI全部的宗旨就是儘可能簡化遠程接口對象的使用。

Java RMI極大地依賴於接口。在需要創建一個遠程對象的時候,程序員通過傳遞一個接口來隱藏底層的實現細節。客戶端得到的遠程對象句柄正好與本地的根代碼連接,由後者負責透過網絡通信。這樣一來,程序員只需關心如何通過自己的接口句柄發送消息。

接口的兩種常見實現方式是:

(1)最初使用JRMP(Java Remote Message Protocol,Java遠程消息交換協議)實現;

(2)此外還可以用與CORBA兼容的方法實現。

RMI一般指的是編程接口,也有時候同時包括JRMP和API(應用程序編程接口),而RMI-IIOP則一般指RMI接口接管絕大部分的功能,以支持CORBA的實現。最初的RMI API設計爲通用地支持不同形式的接口實現。後來,CORBA增加了傳值(pass by value)功能,以實現RMI接口。然而RMI-IIOPJRMP實現的接口並不完全一致。

2.RMI實例

RMI相關的類和接口都在jdk java.rmi包中。

RMI的基礎是接口,RMI構架基於一個重要的原理:定義接口和定義接口的具體實現是分開的。

下面我們通過具體的例子,建立一個簡單的遠程計算服務和使用它的客戶程序

一個正常工作的RMI系統由下面幾個部分組成:
遠程服務的接口定義

遠程服務接口的具體實現

Stub 和 Skeleton 文件

一個運行遠程服務的服務器

一個RMI命名服務,它允許客戶端去發現這個遠程服務

類文件的提供者(一個HTTP或者FTP服務器

一個需要這個遠程服務的客戶端程序

下面我們一步一步建立一個簡單的RMI系統。首先在你的機器裏建立一個新的文件夾,以便放置我們創建的文件,爲了簡單起見,我們只使用一個文件夾存放客戶端和服務端代碼,並且在同一個目錄下運行服務端和客戶端。如果所有的RMI文件都已經設計好了,那麼你需要下面的幾個步驟去生成你的系統:

   1、   編寫並且編譯接口的Java代碼

 2、   編寫並且編譯接口實現的Java代碼

 3、   從接口實現類中生成 Stub 和 Skeleton 類文件

 4、   編寫遠程服務的主運行程序

 5、   編寫RMI的客戶端程序

 6、   安裝並且運行RMI系統

2.1 接口

第一步就是建立和編譯服務接口的Java代碼。這個接口定義了所有的提供遠程服務的功能,下面是源程序:

/**
 * 
 */
package com.renren;

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

/**
 * @author root
 * 
 */

public interface CalculatorRemoteInterface extends Remote {

	//! 注意,這個接口繼承自Remote,每一個定義的方法都必須拋出一個RemoteException異常對象
	public abstract int add(int a, int b) throws RemoteException;

}

注意,這個接口繼承自Remote,每一個定義的方法都必須拋出一個RemoteException異常對象

2.2 實現類

package com.renren;

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

@SuppressWarnings("serial")
// 繼承 UnicastRemoteObject 遠程對象 這個一定要寫 否則 服務器啓動報異常
public class CalculatorRemoteObject extends UnicastRemoteObject implements
        CalculatorRemoteInterface {

    public CalculatorRemoteObject() throws RemoteException {
        super();
        // TODO Auto-generated constructor stub
    }

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

    // @Override
    public int add(int a, int b) {
        // TODO Auto-generated method stub
        return a + b;
    }

}


2.3 打包

將上述兩個java文件編譯打包爲rmi-test.jar

在編寫server和client程序時需要依賴該jar包

2.4 Server

Server做的事很簡單,代碼註釋很詳細...

package com.test;

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

public class Server {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {
            // 定義遠程接口CalculatorRemoteInterface對象 用於綁定在服務器註冊表上 該接口由CalculatorRemoteObject類實現
            CalculatorRemoteInterface remoteObject = new CalculatorRemoteObject();
            // 定義一個端口號
            int port = 9999;
            // 創建一個接受對特定端口調用的遠程對象註冊表 註冊表上需要接口一個指定的端口號
            LocateRegistry.createRegistry(port);
            // 定義服務器遠程地址URL格式
            String serverAddress = "rmi://10.2.185.197:" + port+"/calculator" ;
            // 綁定遠程地址和接口對象
            Naming.bind(serverAddress, remoteObject);
            // 如果啓動成功 則彈出如下信息
            System.out.println(">>>服務器啓動成功");
            System.out.println(">>>請啓動客戶端進行連接訪問");
        } catch (MalformedURLException e) {
            System.out.println("地址出現錯誤!");
            e.printStackTrace();
        } catch (AlreadyBoundException e) {
            System.out.println("重複綁定了同一個遠程對象!");
            e.printStackTrace();
        } catch (RemoteException e) {
            System.out.println("創建遠程對象出現錯誤!");
            e.printStackTrace();
        }
    }
    
}

2.5 Client

package com.test;

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

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		try {
			CalculatorRemoteInterface calculator=
					(CalculatorRemoteInterface) Naming.lookup("rmi://10.2.185.197:9999/calculator");
			System.out.println("5+3="+calculator.add(5, 3));
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NotBoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

2.6 測試

1.在一臺機器上啓動Server

2.在兩外一臺機器上啓動Client,可以看到Client成功調用了Server的CalculatorRemoteObject對象的add方法.

2.7 名詞解釋 Stub Skeleton

http://blog.sina.com.cn/s/blog_4ab0d57401009n4a.html

Stub(存根)和Skeleton(骨架)
  Stub和Skeleton是經過rmic命令生成的,我們的程序要通過遠程調用,底層一定是套接字的字節傳輸,要一個對象序列化成爲一個字節數組,傳輸到服務器或者客戶端的對端之後,再把該對象反序列化成爲對應的對象,這些網絡傳輸的過程要求安全,穩定等等非常麻煩的操作,Stub駐留客戶端,承擔着代理遠程對象的實現者的角色,Skeleton類幫助遠程對象與Stub再RMI連接上進行通信。RMI的客戶與Stub進行交換,Stub與Skeleton通信,Skeketon負責與服務器進行交互,因此有了Stub和Skeleton之後我們就不需要實現底層通信的細節,我們進行的遠程調用,只需要通過接口對方法進行操作即可,使分佈式調用實現了位置上的透明,即:遠程調用就像本地調用一樣。
   通俗的說,RMI的代理模式迫使方法調用必須通過充當替身的代理對象,即Stub和Skeleton,由這些代理對象將方法傳遞給實際的對象。
   在遠程虛擬機中,每個遠程對象都有一個Skeleton對應,Skeleton負責將調用分配給實際的遠程對象來實現,他的工作是在服務器端的。在JDK1.2版本以上的環境中不需要Skeleton,因爲Jdk1.2推出了附加的Skeleton,並且對底層的協議也進行了修改,性能上有很大提升,默認的rmic命令是不會產生Skeleton類的,如果要想生成Skeleton類可以使用rmic-v1.1 即可。
   Stub的操作任務:
    a、初始化與包含遠程對象遠程虛擬機的連接
    b、與遠程虛擬機參數進行編組,也就是調度,包括參數的寫入以及傳輸
    c、等待方法的調用結果
    d、解編返回值和返回的異常。也就是讀取服務器上的返回值,也稱爲反調度
    e、將結果返回給調用程序。
   Skeleton對應的任務:
    a、讀取遠程方法的參數,也就是解編
    b、調用實際遠程對象上的方法
    c、將結果或者異常組編(寫入並傳輸)給調用程序。
   Stub與Skeleton的關係以及操作是對應的關係。只要我們有編譯好的遠程對象的類,就可以調用jdk的rmic命令來生成stub和skeleton了,關鍵是我們需要理解Stub是客戶端應用程序的代理,Skeleton是服務器端應用程序的代理,他們之間協作完成客戶端與服務器之間的方法調用時的通信。
   瞭解者兩個概念之後,我們接下來需要了解JNDI以及RMI註冊表兩個概念,等剩下的兩個概念瞭解了,我們基本上對RMI就可以入門了。
 

參考:

http://blog.csdn.net/wangxingbao4227/article/details/6842951

http://blog.csdn.net/tin591/article/details/8117198

http://hi.baidu.com/dl_linfeng/item/330036304422c65c80f1a778




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