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架构设计与实现原理】一书,仅作为学习笔记,用于技术交流,其商业版权由原作者保留,推荐大家购买图书研究,转载请保留原作者,谢谢!

       

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值