Hadoop源码分析笔记(三):Hadoop远程过程调用

来源:互联网 发布:ubuntu 移动硬盘 编辑:程序博客网 时间:2024/06/05 10:56

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)); }@Overridepublic void readFields(DataInput in) throws IOException {    this.filename = Text.readString(in);    this.time = in.readLong();}@Overridepublic 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() {}@Overridepublic IPCFileStatus getFileStatus(String filename) {IPCFileStatus status=new IPCFileStatus(filename);System.out.println("Method getFileStatus Called, return: "+status);return status;}@Overridepublic 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架构设计与实现原理】一书,仅作为学习笔记,用于技术交流,其商业版权由原作者保留,推荐大家购买图书研究,转载请保留原作者,谢谢!

       

 

原创粉丝点击