如下是一個使用Hadoop IPC實現客戶端調用服務器端方法的示例,功能是返回服務器端的一個文件信息。
1 文件信息類IPCFileStatus
代碼如下所示:
package org.seandeng.hadoop.ipc;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Date;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
public class IPCFileStatus implements Writable {
private String filename;
private long time;
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));
}
public void readFields(DataInput in) throws IOException {
this.filename = Text.readString(in);
this.time = in.readLong();
}
public void write(DataOutput out) throws IOException {
Text.writeString(out, filename);
out.writeLong(time);
}
}
由於IPCFileStatus類的對象需要從服務器端傳到客戶端,所以就需要進行序列化,Writable接口就是Hadoop定義的一個序列化接口。
由於客戶端要調用服務器的方法,所以客戶端需要知道服務器有哪些方法可以調用,在IPC中使用的是定義接口的方法,如定義一個IPC接口,客戶端和服務器端都知道這個接口,客戶端通過IPC獲取到一個服務器端這個實現了接口的引用,待要調用服務器的方法時,直接使用這個引用來調用方法,這樣就可以調用服務器的方法了。
2 接口IPCQueryStatus
定義一個服務器端和客戶端接口IPCQueryStatus如下所示:
package org.seandeng.hadoop.ipc;
import org.apache.hadoop.ipc.VersionedProtocol;
public interface IPCQueryStatus extends VersionedProtocol {
IPCFileStatus getFileStatus(String filename);
}
在接口IPCQueryStatus中,定義了一個getFileStatus(String filename)方法,根據文件名得到一個IPCFileStatus對象,注意到IPCQueryStatus接口繼承自接口 org.apache.hadoop.ipc.VersionedProtocol接口,VersionedProtocol接口是Hadoop IPC接口必須繼承的一個接口,它定義了一個方法getProtocolVersion(),用於返回服務器端的接口實現的版本號,有兩個參數,分別是協議接口對應的接口名稱protocol和客戶端期望服務器的版本號clientVersion,主要作用是檢查通信雙方的接口是否一致,VersionedProtocol的代碼如下:
package org.apache.hadoop.ipc;
import java.io.IOException;
/**
* Superclass of all protocols that use Hadoop RPC.
* Subclasses of this interface are also supposed to have
* a static final long versionID field.
*/
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;
}
3 實現類IPCQueryStatusImpl
定義好了接口,那麼在服務器端就需要有一個接口的實現類,用於實現具體的業務邏輯,下面的IPCQueryStatusImpl類實現了IPCQueryStatus接口,僅僅簡單實現了IPCQueryStatus規定兩個方法。
package org.seandeng.hadoop.ipc;
import java.io.IOException;
public class IPCQueryStatusImpl implements IPCQueryStatus {
public IPCQueryStatusImpl() {}
public IPCFileStatus getFileStatus(String filename) {
IPCFileStatus status=new IPCFileStatus(filename);
System.out.println("Method getFileStatus Called, return: "+status);
return status;
}
/**
* 用於服務器與客戶端,進行IPC接口版本檢查,再服務器返回給客戶端時調用,如果服務器端的IPC版本與客戶端不一致
* 那麼就會拋出版本不一致的異常
*/
public long getProtocolVersion(String protocol, long clientVersion) throws IOException {
System.out.println("protocol: "+protocol);
System.out.println("clientVersion: "+clientVersion);
return IPCQueryServer.IPC_VER;
}
}
getFileStatus()方法根據參數filename創建了一個IPCFileStatus對象,getProtocolVersion()方法返回服務器端使用的接口版本。接口和實現類都完成之後就可以用客戶端和服務器進行通信了。
4 IPCQueryServer類
服務器端進行一些成員變量的初始化,然後使用Socket綁定IP,然後在某個端口上監聽客戶端的請求。IPCQueryServer類相關代碼如下所示:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.ipc.Server;
public class IPCQueryServer {
public static final int IPC_PORT = 32121;
public static final long IPC_VER = 5473L;
public static void main(String[] args) {
try {
Configuration conf = new Configuration();
IPCQueryStatusImpl queryService=new IPCQueryStatusImpl();
System.out.println(conf);
Server server = RPC.getServer(queryService, "127.0.0.1", IPC_PORT, 1, false, conf);
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();
}
}
}
在服務器端先創建一個IPCQueryStatusImpl的對象,傳遞到RPC.getServer()方法中。服務器端使用RPC.getServer()方法穿給創建服務器端對象server,代碼中RPC.getServer()方法的幾個參數說明如下:
· 第一個參數queryService標識該服務器對象對外提供的服務對象實例,即客戶端所要調用的具體對象,下面客戶端的代碼調用的接口如此對應;
· 第二個參數"127.0.0.1"表示監綁定所有的IP地址;
· 第三個參數IPC_PORT表示監聽的端口;
· 第四個參數1表示Server端的Handler實例(線程)的個數爲1
· 第五個參數false表示不打開調用方法日誌;
· 第六個參數是Configuration對象,用於定製Server端的配置。
創建Server對象之後,調用Server.start()方法開始監聽客戶端的請求,並根據客戶端的請求提供服務。
5 請求類IPCQueryClient
客戶端需要先獲取到一個代理對象,然後才能進行方法調用,在IPC中,使用RPC.getProxy()方法獲取代理對象。客戶端的代碼如下:
package org.seandeng.hadoop.ipc;
import java.net.InetSocketAddress;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
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("Z:\\temp\\7c64984cf5c3410fbe28037865d010a3.pdf");
System.out.println(status);
RPC.stopProxy(query);
} catch (Exception e) {
e.printStackTrace();
}
}
}
客戶端的代碼很簡單,首先構造一個要請求服務器的網絡地址(IP和端口),然後通過RPC.getProxy()方法獲取到一個IPCQueryStatus對象,然後進行相應的方法調用。其中客戶端代碼中RPC.getProxy()方法的參數說明如下:
· 第一個參數是IPC接口對象,可以通過IPC接口的靜態成員class直接獲得。接口的靜態成員class保存了該接口的java.lang.Class實例,它表示正在運行的Java應用程序中的類和接口,提供一系列與Java反射相關的重要功能;
· 第二個參數是接口版本,由於接口會根據需求不斷地進行升級,形成多個版本的IPC接口,如果客戶端和服務器端使用的IPC接口版本不一致,結果將是災難性的,所以在建立IPC時,需要對IPC的雙方進行版本檢查;
· 第三個參數是服務器的Socket地址,用於建立IPC的底層TCP連接;
· 第四個參數是Configuration對象,用於定製IPC客戶端參數。
6 執行結果
客戶端的代碼編寫完成之後就可以運行程序了,先啓動服務器端,再運行一個客戶端,就完成了一次客戶端調用服務器的過程,客戶端調用了服務器端 IPCQueryStatusImpl對象的getFileStatus()方法,服務器端返回了方法調用結果即IPCFileStatus對象。服務器端和客戶端執行日誌如下所示:
服務器端:
2014-11-26 13:00:49,147 WARN conf.Configuration (Configuration.java:<clinit>(191)) - DEPRECATED: hadoop-site.xml found in the classpath. Usage of hadoop-site.xml is deprecated. Instead use core-site.xml, mapred-site.xml and hdfs-site.xml to override properties of core-default.xml, mapred-default.xml and hdfs-default.xml respectively Configuration: core-default.xml, core-site.xml 2014-11-26 13:00:50,124 INFO ipc.Server (Server.java:run(328)) - Starting SocketReader 2014-11-26 13:00:50,222 INFO ipc.Server (Server.java:run(598)) - IPC Server Responder: starting 2014-11-26 13:00:50,223 INFO ipc.Server (Server.java:run(434)) - IPC Server listener on 32121: starting Server ready, press any key to stop 2014-11-26 13:00:50,224 INFO ipc.Server (Server.java:run(1358)) - IPC Server handler 0 on 32121: starting protocol: org.seandeng.hadoop.ipc.IPCQueryStatus clientVersion: 5473 Method getFileStatus Called, return: File: Z:\temp\7c64984cf5c3410fbe28037865d010a3.pdf Create at Wed Nov 26 13:01:02 CST 2014 |
客戶端:
Interface name: org.seandeng.hadoop.ipc.IPCQueryStatus Interface name: getFileStatus 2014-11-26 13:00:59,790 WARN conf.Configuration (Configuration.java:<clinit>(191)) - DEPRECATED: hadoop-site.xml found in the classpath. Usage of hadoop-site.xml is deprecated. Instead use core-site.xml, mapred-site.xml and hdfs-site.xml to override properties of core-default.xml, mapred-default.xml and hdfs-default.xml respectively File: Z:\temp\7c64984cf5c3410fbe28037865d010a3.pdf Create at Wed Nov 26 13:01:02 CST 2014 |