首页 > 分布式 > Hadoop > Hadoop RPC机制及HDFS源码分析
2015
04-28

Hadoop RPC机制及HDFS源码分析

HDFS的源码中存在大量的远程过程调用(RPC),在深入研究HDFS源码之前,必须理解Hadoop RPC机制。

一、Hadoop RPC

网络通信模块是分布式系统中最底层的模块,它支撑了上层分布式环境下复杂的进程间通信(Inter-Process Communication,IPC)逻辑,是所有分布式系统的基础。如何隐藏底层通信的细节,是实现分布式系统访问透明性的重要议题。(透明性在后面会不断解释)

1984年,Birrell和Nelson将远程过程调用(Remote Procedure Call,RPC)引入分布式计算领域。RPC是一种常用的分布式网络通信协议,它允许运行于一台计算机的程序调用另一台计算机的子程序,同时将网络的通信细节隐藏起来,使得用户无须额外地为这个交互作用编程。RPC使得开发分布式程序更加容易。

随着RPC的发展,出现了大量的相关技术,如XML-RPC、JSON-RPC、CORBA和RMI等等。Hadoop RPC是RPC的一种简单实现,它是Hadoop中多个分布式子系统(如HDFS、MapReduce、Yarn等)公用的网络通信模块。

RPC中的访问透明性,是指当用户在一台计算机的程序调用另外一台计算机上的子程序时,用户自身不应感觉到其间涉及跨机器间的通信,而是感觉像是在执行一个本地调用。

1、什么是远程过程调用?

首先从字面上解释,“过程”在Java中指的就是对象中的方法,“远程”是指不同机器上的进程(狭义),或者不同的进程(广义)(为了简单,下文不对这种情况进行说明)。因此,RPC就是允许程序调用其他机器上的对象方法

RPC是属于典型的C/S结构,提供服务的一方称为server,请求服务的一方称为client。server端提供对象方法供client端调用,被调用的对象方法的执行发生在server端

一个典型的RPC框架的架构如下(摘自《Hadoop技术内幕——YARN》):

04. Hadoop RPC机制及HDFS源码分析846

在该架构中,主要包括如下几个部分:

  • 通信模块:我将它理解成Socket,在客户端和服务端之间传递请求和应答消息。(请求 – 响应)
  • Stub程序:我将它理解成代理对象,用于保证RPC的透明性。在客户端,它表现得就像一个本地程序,但不直接执行本地调用,而是将请求信息通过网络模块发送给服务端,当服务端发送应答后,它会解码对应结果。在服务端,Stub程序依次进行解码请求消息中的参数、调用相应的服务过程和编码应答结果的返回值等处理。
  • 调度程序:调度程序接受来自通信模块的请求消息,并根据其中的标识选择一个Stub程序进行处理。
  • 客户程序/服务过程:请求的发出者和请求的处理者。

通常而言,一个RPC请求从发送到获取响应结果,所经历的步骤如下:

① 客户程序以本地方式调用系统产生的Stub程序

② 该Stub程序将函数调用信息按照网络通信模块的要求封装成消息包,并交给通信模块发送到远程服务器端。

③ 远程服务器端接收此消息后,将此消息发送给相应的Stub程序

④ Stub程序拆封消息,形成被调过程要求的形式,并调用对应函数

⑤ 服务端执行被调用函数,并将结果返回给Stub程序

⑥ Stub程序将此结果封装成消息,通过网络通信模块逐级地传送给客户程序。

2、认识Hadoop RPC框架

上一小节解释了什么是RPC,并给出了一个通用的RPC架构。Hadoop RPC是对RPC的一种简单实现,它有如下特点:

  • 透明性:这是所有RPC框架最根本的特点,即保证程序在跨机器调用对象方法时,客户程序中不应感觉到其间涉及跨机器间的通信,而感觉像是在执行一个本地调用。
  • 高性能Hadoop各个系统(如HDFS、MapReduce、YARN等)均采用了Master/Slave结构,其中,Master实际上是一个RPC server,它负责响应集群中所有Slave发送的服务请求。为了保证Master的并发处理能力,RPC server应是一个高性能服务器,能够高效地处理来自多个client的并发RPC请求。
  • 可控性:JDK中已经自带了一个RPC框架——RMI(远程方法调用),之所以不直接使用该框架,主要是考虑到RPC是Hadoop最底层最核心的模块之一,保证其轻量级、高性能和可控性显得尤为重要,而RMI是一个重量级框架且用户可控之处太少。(Doug Cutting就是这样描述Hadoop RPC设计动机的)

3、一个Hadoop RPC框架的Demo

通过一个Demo程序可以更直观的了解Hadoop RPC,该Demo程序分为两部分:rpc-server和rpc-client,其中server端向client提供login服务,如client调用login(“flyne”)时,服务器端应返回 “Hello, flyne. Welcome you!” 字符串。项目的基本信息如下:

360软件小助手截图20150427234818

下面是开发、运行该Demo程序的详细过程:

1)搭建rpc-server开发环境

rpc-server项目在CentOS虚拟机上开发,环境的搭建包括安装CentOS虚拟机、JDK、Eclipse以及网络配置,当然这些步骤太基本了,本文不再赘述,直接在Eclipse中新建rpc-server项目,然后添加项目所需jar包。jar包添加有两种方式:

  • 通过build path直接添加jar包:RPC框架在Hadoop Common模块中,因此RPC程序只需添加HADOOP_HOME/share/hadoop/common目录下的jar包即可。
  • 如果你的项目是个Maven项目,直接在pom.xml文件中添加hadoop-common的依赖即可。

本文程序采用第一种方式管理jar包。

2)定义RPC协议:LoginServiceInterface接口

RPC协议是client端和server端之间的通信接口,它定义了server端对外提供的服务接口。

public interface LoginServiceInterface {
	//协议版本号,不同版本号的client和server之间不能相互通信
	static final long versionID = 1L;

	String login(String name);
}

3)LoginServiceImpl:server端的服务实现

Hadoop RPC协议通常是一个Java接口,定义了server端对外提供的服务接口,需要在server端进行实现。

/**
 * server端的协议实现中不需要关注Socket通信
 */
public class LoginServiceImpl implements LoginServiceInterface {

	public String login(String name) {
		return "Hello, " + name + ". Welcome you!";
	}

}

4)LoginRpcServer:构造并启动RPC server

/**
 * RPC server
 */
public class LoginRpcServer {
	public static void main(String[] args) throws Exception {
		//创建RPC.Builder实例builder,用于构造RPC server
		RPC.Builder builder = new RPC.Builder(new Configuration());
		//向builder传递一些必要的参数,如主机、端口号、真实业务逻辑实例、协议接口
		builder.setBindAddress("hadoop01").setPort(10000)
				.setInstance(new LoginServiceImpl())
				.setProtocol(LoginServiceInterface.class);

		//构造RPC server
		Server server = builder.build();
		//启动server
		server.start();
	}
}

5)搭建rpc-client开发环境

rpc-client项目在win7宿主机上开发,在eclipse中新建一个java项目rpc-client,项目所需jar包的获取方式同1)。

此外还要将rpc-server中的LoginServiceInterface协议接口拷贝至rpc-client。

6)LoginController:调用server端的login过程

/**
 * LoginController is a RPC client
 */
public class LoginController {
	public static void main(String[] args) throws Exception {
		//☆通过RPC拿到LoginServiceInterface的代理对象
		LoginServiceInterface loginService =
				RPC.getProxy(LoginServiceInterface.class, 1L, new InetSocketAddress("hadoop01", 10000), new Configuration());

		//像本地调用一样调用服务端的方法
		String result = loginService.login("flyne");
		System.out.println(result);

		//关闭连接
		RPC.stopProxy(loginService);
	}
}

客户端代码的核心在于RPC.getProxy(),它返回的代理对象就是服务器端对象的代理,内部是使用java.lang.reflect.Proxy实现的。

至此,第一个Hadoop RPC的Demo程序开发完毕。运行起来吧,Yo~

1)启动rpc server

运行LoginRpcServer程序,使server端处于监听状态,等待client端请求到达。查看rpc server运行是否正常有如下两种方式:

  • 通过netstat -natp命令查看10000端口是否被占用(笨办法)
  • 通过jps命令查看是否有LoginRpcServer进程

扩展:还记得启动HDFS、YARN之后使用jps查看相关进程是否启动成功不?原来,NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager这些进程都是作为一个RPC server,并对外提供RPC服务的。

2)在client端调用login过程

运行LoginController程序,在客户端的控制台输出 “Hello, flyne. Welcome you!”。说明RPC调用成功。

Question:远程过程调用的对象方法(login方法)是在server端执行还是在client端执行的?

Answer:RPC调用时,被调用的对象方法的执行发生在server端。为了证明,可以在LoginServiceImpl的login方法中打印一句话,在发生RPC调用时,看这句话是在client端的控制台打印还是server端。这和上面Hadoop RPC框架特点中讲的高效性是一回事。

Question:为什么要将开发过程2)中的接口称为RPC协议???

Answer:首先要明白通信协议是干嘛的,它是双方实体完成通信或服务所必须遵循的规则和约定,说白了就是规定通信过程中我给你发送什么数据,你返回什么,而这正好对应了一个方法中的形参和返回值。因此将一个接口称作RPC协议是合理的,RPC client发送请求时,和server的接口应该保持一致。

Summary:使用Hadoop RPC的4个步骤(定义RPC协议、实现RPC协议、构造并启动RPC server、构造RPC client并发送RPC请求)

4、Hadoop RPC内部设计原理

通过第3节的学习,我们学会了利用Hadoop RPC搭建一个高效的C/S通信模型。推荐读者在接下来的学习中,深入到Hadoop RPC内部(主要围绕RPC、Client、Server三个类展开),掌握其设计原理及技巧。

本文并不打算讲解这部分内容,有兴趣的童鞋可以阅读《Hadoop技术内部——YARN》一书的3.3.5节:Hadoop RPC类详解,传送:http://book.51cto.com/art/201312/422044.htm


留下一个回复

你的email不会被公开。