HDFS写文件的流程


HDFS写文件的流程浅析

 

提到大数据,我们一定绕不开Hadoop,某种意义上Hadoop就代表了大数据这一领域。作为Hadoop的两大核心之一的HDFS,随着大数据的快速发展也越来越被更多的人认识和使用。今天我们就简单剖析一下HDFS写文件的流程。

 

 

 

 

如图所示,HDFS写文件具体流程如下:

1.客户端端首先通过DistributedFileSystem对象的create方法创建一个FSDataOutputStream(输出流)对象;

2.客户端通过DistributedFileSystem对象向NameNode发起一次RPC调用,在HDFS的Namespace中创建一个文件条目;

3.通过FSDataOutputStream对象,向DataNode写入数据,数据首先被写入FSDataOutputStream对象内部的Buffer中,然后数据被分割成一个个Packet数据包;

4.以Packet最小单位,基于Socket连接发送到按特定算法选择的HDFS集群中一组DataNode中的一个节点上,在这组DataNode组成的Pipeline(数据量管道)上依次传输Packet;

5.这组DataNode组成的Pipeline反方向上,发送ack,最终由Pipeline中第一个DataNode节点将Pipeline ack发送给客户端;

6.完成向文件写入数据,客户端在FSDataOutputStream对象上调用close方法,关闭流;

7.调用DistributedFileSystem对象的complete方法,通知NameNode文件写入成功。

 

 

下面以2个问题来细化上述写文件的流程:

 

Q1:数据往哪写? 

 

对于一个上千节点Hadoop集群,在写文件是应该往哪几个DataNode里面写呢?这就涉及到HDFS副本放置策略相关内容了。HDFS采用多副本技术保证数据的高可用,在实现副本放置位置的选取过程中,需要考虑2方面因素:可靠性与网络带宽。以3副本为例,有如下情形:

 

情形1:3副本放置在同一台机器上,此种情形可靠性差,如果这台机器宕机,则数据所有副本都不可用;但由于所有副本在同一台机器上,写入数据时都是在机器内部进行传输所以网络带宽开销最小。

 

 

 

 

情形2:数据副本全部分散到不同的机架中。此种情形数据可靠性最高,即便是存储数据副本的两个机架都出现故障,还是有一个副本可用;但由于数据副本分散到不同的机架中,导致数据的读写需要跨交换机,网络带宽的开销较大。

 
 

 

 

 

情形3:将情形1与情形2进行折中,得到如下存储模式:

1.对于副本1,如果写请求方所在机器是其中一个DataNode,则直接存放在本地,否则随机在集群中选择一个DataNode;

2.对于副本2,第二个副本存放于不同第一个副本的所在的机架;.

3.对于副本3,第三个副本存放于第二个副本所在的机架,但是属于不同的节点。

 

这样即便是有一个存储数据副本的机架故障,数据依然可用;同时第二副本和第三副本在同一个机架中,数据的读写较情形2需要的网络带宽更小。

 

 

 

 

目前HDFS中副本放置策略的实现主要有两种方式:

BlockPlacementPolicyDefault, 

BlockPlacementPolicyWithNodeGroup。

 

集群默认采用BlockPlacementPolicyDefault方式,这里以此种实现为例。

分析代码可知,选择策略的核心方法为chooseTargets,具体流程如下:

 

1.如果发生写请求的客户端不是集群内部的DataNode节点,则在集群内随机分配一个节点作为首节点

2.如果发生写请求的客户端本身就是集群中的一个DataNode,则第一个副本就放置在本节点上,这样写数据就是本地写操作,避免了网络带宽的消耗;如果本地节点不满足放置该副本的条件,则退而求其次,选择一个与本地同机架的DataNode节点,如果同机架上的所有节点都不满足条件,则在集群内随机分配一个节点作为首节点

3.如果已经成功找到首节点,则在与首节点不同的机架内寻找第二个节点。如果找到符合要求的节点,则第二个节点分配完毕;如果在远程机架内没有找到合适的节点,则在首节点同机架内寻找,如果找到则第二节点分配完毕。

4.如果前两个节点分配完毕,则进行第三个节点的分配。首选判断前两个节点是否在同一个机架内,如果是,则在远程机架中寻找满足条件的节点,如果找到,则第三个节点分配完毕,如果未找到;如果前两个节点不在同一个机架内,则在第二个节点的机架内寻找,如果找到满足条件的节点,则分配完毕,如果未找到,则在集群内随机分配一个节点作为第三节点。

5.对于副本数大于3的情况,剩余节点则在集群内随机选取。

 

 

 
 

 

当NameNode选择出3个DataNode节点以后会将他们组成一个Pipeline返回给客户端用以数据写入,那Pipeline是怎样构建的呢?其中的根据又是什么?这就涉及到HDFS中的另一块内容——机架感知。在NameNode内部,将集群中所有的DataNode以一个树的形式组织起来,如下图所示,DataNode节点之间可以用距离来表示两个节点的远近程度,每对DataNode节点间的距离计算方式是通过寻找最近的公共祖先所需要的距离作为最终的结果,是一个典型的LCA最近公共祖先算法实现。

 

 

 

 

 

确定了节点见的距离的定义之后,再来看Pipeline的构建,其构建目标是使得Pipeline节点间距离之和最小。假设我们有一个客户端和选取出的3个Nodes:DataNode1、DataNode2、DataNode3,则Pipeline生成过程如下:

(1)从DataNode1、DataNode2、DataNode3中找出与客户端之间距离最短的节点,假设为DataNode2;

(2)从DataNode1、DataNode3中找出与DataNode2之间距离最短的节点,假设为DataNode3;

(3)从DataNode1中找出与DataNode4之间距离最短的节点,为DataNode1;

 

最后我们即得到一个“节点距离之和最小”的管道:客户端、DataNode2、DataNode3、DataNode1。这其实是一个经典的图论算法——TSP旅商算法的简单实现。到此为止,客户端获得了一个由3个节点组成的Pipeline,就可以开始往里面写入数据了。

 

Q2:数据怎样写 

 

我们都知道HDFS中的文件是以Block块的形式进行存储的,那在写入过程中是否也是以Block为单位进行的呢?其实不然,通过分析DFSOutputStream代码我们可以发现,客户端会先将数据写到输出流内部的缓冲区中,然后见数据组装成一个个大小为64KB的Packet(数据包),而每个Packet又是由多个Chunk组成,每个Chunk由512字节数据和4字节校验位组成。下图所示为一个Packet的帧结构。

 

 

 

 

当写入的数据达到一个Packet长度时,DFSOutputStream就会构造一个Packet并放入一个队列dataQueue中,同时输出流的内部线程DataStreamer会不断从dataQueue中取出Packet发送到Pipeline中第一个DataNode上,并将该Packet移至另一个队列ackQueue中。

输出流的另一个内部线程ResponseProcessor则接收来自下游节点的ack信息,如果是一个成功的ack,则表示Pipeline中的节点都接收到了次Packet,ResponseProcessor就会将改Packet从ackQueue中删除。在发送过程中,如果发生错误,所有未完成的Packet都会从ackQueue队列中移除掉,然后重新创建一个新的Pipeline,排除掉出错的那些DataNode节点,接着DataStreamer线程继续从dataQueue队列中发送Packet。写数据的示意图如下:

 

 

 

 

 

在DataNode端同样以Packet为单位进行写入与转发。DataNode的BlockReceiver类负责从数据流管道中的上游节点接收Packet,然后保存到当前数据节点中,并将Packet转发至数据流管道的下游节点。同时BlockReceiver还会启动一个PacketResponder内部线程用于处理响应信息,该线程会接收来自下游节点的Ack响应,然后在响应中加入本机节点的状态信息并将Acl转发至上游节点。

 

 

 

 

本文简单剖析了HDFS写文件的流程,其中很多细节部分没有详细展开,HDFS写文件的容错机制也没有介绍,有兴趣的同学可以结合源码进一步学习。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM