Hadoop源碼分析筆記(三):Hadoop遠程過程調用

Hadoop遠程過程調用

遠程過程調用(RPC)

        作爲分佈式系統,Hadoop中各個實體間存在着大量的交互,遠程過程調用(Remote Procedure Call,RPC)讓用戶可以像調用本地方法一樣調用另外一個應用程序提供的服務,而不必設計和開發相關的信息發送、處理和接收等具體代碼,它提高了程序的互操作性。

        簡要來說,RPC就是允許程序調用位於其他機器上的過程(也可以是同一臺機器的不同進程)。但機器A上的進程調用機器B上的進程時,A上的進程被掛起,而B上的被調用的進程開始執行。調用方使用參數將信息傳送給被調用方。然後通過傳回的結果得到信息。在這個過程中A是PRC客戶,B是RPC服務器。同時,編程人員看不到任何消息的傳遞,這個過程對用戶來說是透明的。其行爲如同一個過程到另一個過程的調用一樣。

        RPC引入客戶存根(Client Stub)和服務器骨架(Server Skeleton)來解決系統在有差異的情況下進行參數和結果的傳遞,並對通信雙方的狀態進行監控。

Java遠程方法調用(RMI)

        在某種程度上來看,RMI可以看成是RPC的Java升級版。和RPC一樣,包含RMI的Java應用程序通常包括服務器程序和客戶端程序。它提供了和PRC中類似的

標準的Stub/Skeleton機制。Stub代表可以被客戶端引用的遠程對象,位於客戶端,並保持着遠程對象的接口和方法列表。客戶端應用調用遠程對象時,Stub將調用請求,通過RMI的基礎結構轉發到遠程對象上。接受到調用請求時,服務器端的Skeleton對象處理相關調用“遠方”對象中的所有細節並調用Skeleton對象。

        Java遠程方法調用依賴於Java對象的序列化機制,他將調用的參數和返回值序列化並在網絡中傳遞。

Hadoop遠程過程調用

        Hadoop遠程過程調用實現使用Java動態代理和新輸入/輸出系統(NIO),Hadoop沒有使用前面的Java RMI,而是實現了一套自己獨有的節點間通信機制,理由和Hadoop使用Writable形式的序列化機制類似,有效的IPC(Inter-Process Communication,進程間通信)對於Hadoop來說是至關重要的,Hadoop需要精確控制進程間通信中比如連接、超時、緩存等通信細節,顯然Java RMI達不到這些需求,Hadoop進程間通信機制,結合數據輸出流(DataOutputStram)和數據輸入流(DataInputStram)的Writable序列化機制,以及一個簡潔的、低消耗的遠程過程調用機制。

        Java RMI的開發從遠程接口的定義開始,遠程接口必須繼承java.rmi.Remote;在Hadoop遠程過程調用中,也是通過一個IPC接口開始進行開發。Hadoop IPC接口必須繼承自org.apche.hadoop.ipc.VersionedProtocol接口,代碼如下:

   

public interface VersionedProtocol {
  
  /**
   * Return protocol version corresponding to protocol interface.
   * @param protocol The classname of the protocol interface
   * @param clientVersion The version of the protocol that the client speaks
   * @return the version that the server will speak
   */
  public long getProtocolVersion(String protocol, 
                                 long clientVersion) throws IOException;
}


        在Hadoop中,這個接口不是一個聲明性接口,實現該接口對應的接口都必須實現這個方法。它有兩個參數,分別是協議對應的接口名字和客戶端期望的協議的版本號。方法則返回服務器端的接口實現的版本號。在建立IPC時,getProtocolVersion()方法用戶檢查通信的雙方,保證他們使用了相同版本的接口。下面貼上一段利用Hadoop IPC接口實現自己的IPC應用。

   

//需要序列化的類
public class IPCFileStatus implements Writable {	
	private String filename;
    private long time;
    
    static {   // register IPCFileStatus
        WritableFactories.setFactory
            ( IPCFileStatus.class,
              new WritableFactory() {
                  public Writable newInstance() { return new IPCFileStatus(); } } );
    }
    
    public IPCFileStatus() {    	
    }
    
	public IPCFileStatus(String filename) {
		this.filename=filename;
		this.time=(new Date()).getTime();
	}

	public String getFilename() {
		return filename;
	}
	
	public void setFilename(String filename) {
		this.filename = filename;
	}
	
	public long getTime() {
		return time;
	}
	
	public void setTime(long time) {
		this.time = time;
	}
	
	public String toString() {
		return "File: "+filename+" Create at "+(new Date(time)); 
	}

	@Override
	public void readFields(DataInput in) throws IOException {
	    this.filename = Text.readString(in);
	    this.time = in.readLong();		
	}

	@Override
	public void write(DataOutput out) throws IOException {
		Text.writeString(out, filename);
		out.writeLong(time);
	}
}
//接口
public interface IPCQueryStatus extends VersionedProtocol {
	IPCFileStatus getFileStatus(String filename);
}
//實現類
public class IPCQueryStatusImpl implements IPCQueryStatus {	
	protected IPCQueryStatusImpl() {
	}

	@Override
	public IPCFileStatus getFileStatus(String filename) {
		IPCFileStatus status=new IPCFileStatus(filename);
		System.out.println("Method getFileStatus Called, return: "+status);
		return status;
	}

	@Override
	public long getProtocolVersion(String protocol, long clientVersion) throws IOException {
		System.out.println("protocol: "+protocol);
		System.out.println("clientVersion: "+clientVersion);
		return IPCQueryServer.IPC_VER;
	}
}

//服務器端
public class IPCQueryServer {
	public static final int IPC_PORT = 32121;
	public static final long IPC_VER = 5473L;
	
	public static void main(String[] args) {
		try {
			ConsoleAppender append=new ConsoleAppender(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN));
			append.setThreshold(Level.DEBUG);
			BasicConfigurator.configure();

	        IPCQueryStatusImpl queryService=new IPCQueryStatusImpl();
	        
	        Server server = RPC.getServer(queryService, 
	        		                      "0.0.0.0", IPC_PORT, 
	        		                      1, true,
	        		                      new Configuration());
			server.start();
			
			System.out.println("Server ready, press any key to stop");
			System.in.read();
			
			server.stop();
			System.out.println("Server stopped");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
//客戶端
public class IPCQueryClient {
	public static void main(String[] args) {
		try {
			System.out.println("Interface name: "+IPCQueryStatus.class.getName());
			System.out.println("Interface name: "+IPCQueryStatus.class.getMethod("getFileStatus", String.class).getName());
			
			InetSocketAddress addr=new InetSocketAddress("localhost", IPCQueryServer.IPC_PORT);
			IPCQueryStatus query=(IPCQueryStatus) RPC.getProxy(IPCQueryStatus.class, 
					                                           IPCQueryServer.IPC_VER, 
					                                           addr, 
					                                           new Configuration());
			IPCFileStatus status=query.getFileStatus("/tmp/testIPC");
			System.out.println(status);
			RPC.stopProxy(query);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


Hadoop IPC的代碼結構

           Hadoop中與IPC相關的代碼都在org.apache.hadoop.ipc包中,類簡介如下:

           RemoteExecption:遠程異常,應用於IPC客戶端,表示遠程過程調用中的錯誤。

           Status:枚舉類,定義了遠程過程調用的返回結果,包括SUCCESS、ERROR、FATAL等情況。

           VersionedProtocol接口:前面已經介紹過,Hadoop IPC的遠程接口都擴展自VersionedProtocol.

           ConnectionHeader:IPC客戶端與服務器端建立連接時發送的消息頭。

            Client:包含了與IPC客戶端相關的代碼。它的內部類包括:Client.Connection、Client.ConnectionId和Client.Call、Client、ParallelCall等類

            Server:包含了與IPC服務端相關的代碼。它的內部類包括:Server.Connection與Server.Call。【Listener、Handler、Responder】這三個類是對遠程調用的處理它們都繼承自java.lang.Thread類,在各自的線程中運行。

            RPC類:它在Client和Server類的基礎上面實現了Hadoop IPC的功能。

          

          版權申明:本文部分摘自【蔡斌、陳湘萍】所著【Hadoop技術內幕 深入解析Hadoop Common和HDFS架構設計與實現原理】一書,僅作爲學習筆記,用於技術交流,其商業版權由原作者保留,推薦大家購買圖書研究,轉載請保留原作者,謝謝!

       

 

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