HDFS-Architecture剖析
1.概述
从HDFS的应用层面来看,我们可以非常容易的使用其API来操作HDFS,实现目录的创建、删除,文件的上传下载、删除、追加(Hadoop2.x版本以后开始支持)等功能。然而仅仅局限与代码层面是不够的,了解其实现的具体细节和过程是很有必要的,本文笔者给大家从以下几个方面进行剖析:
- Create
- Delete
- Read
- Write
- Heartbeat
下面开始今天的内容分享。
2.Create
在HDFS上实现文件的创建,该过程并不复杂,Client到NameNode的相关操作,如:修改文件名,创建文件目录或是子目录等,而这些操作只涉及Client和NameNode的交互,过程如下图所示:
我们很熟悉,在我们使用Java 的API去操作HDFS时,我们会在Client端通过调用HDFS的FileSystem实例,去完成相应的功能,这里我们不讨论FileSystem和DistributedFileSystem和关系,留在以后在阅读这部分源码的时候在去细说。而我们知道,在使用API实现创建这一功能时是非常方便的,代码如下所示:
public static void mkdir(String remotePath) throws IOException {
FileSystem fs = FileSystem.get(conf);
Path path = new Path(remotePath);
fs.create(path);
fs.close();
}
在代码层面上,我们只需要获取操作HDFS的实例即可,调用其创建方法去实现目录的创建。但是,其中的实现细节和相关步骤,我们是需要清楚的。在我们使用HDFS的FileSystem实例时,DistributedFileSystem对象会通过IPC协议调用NameNode上的mkdir()方法,让NameNode执行具体的创建操作,在其指定的位置上创建新的目录,同时记录该操作并持久化操作记录到日志当中,待方法执行成功后,mkdir()会返回true表示创建成功来结束创建过程。在此期间,Client和NameNode不需要和DataNode进行数据交互。
3.Delete
在执行创建的过程当中不涉及DataNode的数据交互,而在执行一些较为复杂的操作时,如删除,读、写操作时,需要DataNode来配合完成对应的工作。下面以删除HDFS的文件为突破口,来给大家展开介绍。删除流程图如下所示:
在使用API操作删除功能时,我使用以下代码,输入要删除的目录地址,然后就发现HDFS上我们所指定的删除目录就被删除了,而然其中的删除细节和过程却并不一定清楚,删除代码如下所示:
public static void rmr(String remotePath) throws IOException {
FileSystem fs = FileSystem.get(conf);
Path path = new Path(remotePath);
boolean status = fs.delete(path, true);
LOG.info("Del status is [" + status + "]");
fs.close();
}
通过阅读这部分删除的API实现代码,代码很简单,调用删除的方法即可完成删除功能。但它是如何完成删除的,下面就为大家这剖析一下这部分内容。
在NameNode执行delete()方法时,它只是标记即将要删除的Block(操作删除的相关记录是被记录并持久化到日志当中的,后续的相关HDFS操作都会有此记录,便不再提醒),NameNode是被动服务的,它不会主动去联系保存这些数据的Block的DataNode来立即执行删除。而我们可以从上图中发现,在DataNode向NameNode发送心跳时,在心跳的响应中,NameNode会通过DataNodeCommand来命令DataNode执行删除操作,去删除对应的Block。而在删除时,需要注意,整个过程当中,NameNode不会主动去向DataNode发送IPC调用,DataNode需要完成数据删除,都是通过DataNode发送心跳得到NameNode的响应,获取DataNodeCommand的执行命令。
4.Read
在读取HDFS上的文件时,Client、NameNode以及DataNode都会相互关联。按照一定的顺序来实现读取这一过程,读取过程如下图所示:
通过上图,读取HDFS上的文件的流程可以清晰的知道,Client通过实例打开文件,找到HDFS集群的具体信息(我们需要操作的是ClusterA,还是ClusterB,需要让Client端知道),这里会创建一个输入流,这个输入流是连接DataNode的桥梁,相关数据的读取Client都是使用这个输入流来完成的,而在输入流创建时,其构造函数中会通过一个方法来获取NameNode中DataNode的ID和Block的位置信息。Client在拿到DataNode的ID和Block位置信息后,通过输入流去读取数据,读取规则按照“就近原则”,即:和最近的DataNode建立联系,Client反复调用read方法,并将读取的数据返回到Client端,在达到Block的末端时,输入流会关闭和该DataNode的连接,通过向NameNode获取下一个DataNode的ID和Block的位置信息(若对象中为缓存Block的位置信息,会触发此步骤,否则略过)。然后拿到DataNode的ID和Block的位置信息后,在此连接最佳的DataNode,通过此DataNode的读数据接口,来获取数据。
另外,每次通过向NameNode回去Block信息并非一次性获取所有的Block信息,需得多次通过输入流向NameNode请求,来获取下一组Block得位置信息。然而这一过程对于Client端来说是透明的,它并不关系是一次获取还是多次获取Block的位置信息,Client端在完成数据的读取任务后,会通过输入流的close()方法来关闭输入流。
在读取的过程当中,有可能发生异常,如:节点掉电、网络异常等。出现这种情况,Client会尝试读取下一个Block的位置,同时,会标记该异常的DataNode节点,放弃对该异常节点的读取。另外,在读取数据的时候会校验数据的完整性,若出现校验错误,说明该数据的Block已损坏,已损坏的信息会上报给NameNode,同时,会从其他的DataNode节点读取相应的副本内容来完成数据的读取。Client端直接联系NameNode,由NameNode分配DataNode的读取ID和Block信息位置,NameNode不提供数据,它只处理Block的定位请求。这样,防止由于Client的并发数据量的迅速增加,导致NameNode成为系统“瓶颈”(磁盘IO问题)。
5.Write
HDFS的写文件过程较于创建、删除、读取等,它是比较复杂的一个过程。下面,笔者通过一个流程图来为大家剖析其中的细节,如下图所示:
Client端通过实例的create方法创建文件,同时实例创建输出流对象,并通过远程调用,通知NameNode执行创建命令,创建一个新文件,执行此命令需要进行各种校验,如NameNode是否处理Active状态,被创建的文件是否存在,Client创建目录的权限等,待这些校验都通过后,NameNode会创建一个新文件,完成整个此过程后,会通过实例将输出流返回给Client。
这里,我们需要明白,在向DataNode写数据的时候,Client需要知道它需要知道自身的数据要写往何处,在茫茫Cluster中,DataNode成百上千,写到DataNode的那个Block块下,是Client需要清楚的。在通过create创建一个空文件时,输出流会向NameNode申请Block的位置信息,在拿到新的Block位置信息和版本号后,输出流就可以联系DataNode节点,通过写数据流建立数据流管道,输出流中的数据被分成一个个文件包,并最终打包成数据包发往数据流管道,流经管道上的各个DataNode节点,并持久化。
Client在写数据的文件副本默认是3份,换言之,在HDFS集群上,共有3个DataNode节点会保存这份数据的3个副本,客户端在发送数据时,不是同时发往3个DataNode节点上写数据,而是将数据先发送到第一个DateNode节点,然后,第一个DataNode节点在本地保存数据,同时推送数据到第二个DataNode节点,依此类推,直到管道的最后一个DataNode节点,数据确认包由最后一个DataNode产生,并逆向回送给Client端,在沿途的DataNode节点在确认本地写入成功后,才会往自己的上游传递应答信息包。这样做的好处总结如下:
- 分摊写数据的流量:由每个DataNode节点分摊写数据过程的网络流量。
- 降低功耗:减小Client同时发送多份数据到DataNode节点造成的网络冲击。
另外,在写完一个Block后,DataNode节点会通过心跳上报自己的Block信息,并提交Block信息到NameNode保存。当Client端完成数据的写入之后,会调用close()方法关闭输出流,在关闭之后,Client端不会在往流中写数据,因而,在输出流都收到应答包后,就可以通知NameNode节点关闭文件,完成一次正常的写入流程。
在写数据的过程当中,也是有可能出现节点异常。然而这些异常信息对于Client端来说是透明的,Client端不会关心写数据失败后DataNode会采取哪些措施,但是,我们需要清楚它的处理细节。首先,在发生写数据异常后,数据流管道会被关闭,在已经发送到管道中的数据,但是还没有收到确认应答包文件,该部分数据被重新添加到数据流,这样保证了无论数据流管道的哪个节点发生异常,都不会造成数据丢失。而当前正常工作的DateNode节点会被赋予新的版本号,并通知NameNode。即使,在故障节点恢复后,上面只有部分数据的Block会因为Blcok的版本号与NameNode保存的版本号不一致而被删除。之后,在重新建立新的管道,并继续写数据到正常工作的DataNode节点,在文件关闭后,NameNode节点会检测Block的副本数是否达标,在未达标的情况下,会选择一个新的DataNode节点并复制其中的Block,创建新的副本。这里需要注意的是,DataNode节点出现异常,只会影响一个Block的写操作,后续的Block写入不会收到影响。
6.Heartbeat
前面说过,NameNode和DataNode之间数据交互,是通过DataNode节点向NameNode节点发送心跳来获取NameNode的操作指令。心跳发送之前,DataNode需要完成一些步骤之后,才能发送心跳,流程图如下所示:
从上图来看,首先需要向NameNode节点发送校验请求,检测是否NameNode节点和DataNode节点的HDFS版本是否一致(有可能NameNode的版本为2.6,DataNode的版本为2.7,所以第一步需要校验版本)。在版本校验结束后,需要向NameNode节点注册,这部分的作用是检测该DataNode节点是否属于NameNode节点管理的成员之一,换言之,ClusterA的DataNode节点不能直接注册到ClusterB的NameNode节点上,这样保证了整个系统的数据一致性。在完成注册后,DataNode节点会上报自己所管理的所有的Block信息到NameNode节点,帮助NameNode节点建立HDFS文件Block到DataNode节点映射关系(即保存Meta),在完成此流程之后,才会进入到心跳上报流程。
另外,如果NameNode节点长时间接收不到DataNode节点到心跳,它会认为该DataNode节点的状态处理Dead状态。如果NameNode有些命令需要DataNode配置操作(如:前面的删除指令),则会通过心跳后的DataNodeCommand这个返回值,让DataNode去执行相关指令。
7.总结
简而言之,关于HDFS的创建、删除、读取以及写入等流程,可以一言以蔽之,内容如下:
- Create:Client直接与NameNode交互,不涉及DataNode
- Delete:Client将删除指令存于NameNode,DataNode通过心跳获取NameNode的操作指令
- Read:Client通过NameNode获取读取数据的位置,找到DataNode节点对应的Block位置读取数据
- Write:Client通过NameNode后区写数据的位置,找到DataNode节点对应的Block位置进行写入
8.结束语
这篇博客就和大家分享到这里,如果大家在研究学习的过程当中有什么问题,可以加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉!
HDFS-Architecture剖析的更多相关文章
- Hadoop官方文档翻译——HDFS Architecture 2.7.3
HDFS Architecture HDFS Architecture(HDFS 架构) Introduction(简介) Assumptions and Goals(假设和目标) Hardware ...
- 【转载】Hadoop官方文档翻译——HDFS Architecture 2.7.3
HDFS Architecture HDFS Architecture(HDFS 架构) Introduction(简介) Assumptions and Goals(假设和目标) Hardware ...
- HDFS Architecture Notes
[HDFS Architecture Notes] 1.Moving Computation is Cheaper than Moving Data A computation requested b ...
- HDFS数据流-剖析文件读取及写入
HDFS数据流-剖析文件读取及写入 文件读取 1. 客户端通过调用FileSystem对象的open方法来打开希望读取的文件,对于HDFS来说,这个对象是分布式文件系统的一个实例.2. Distrib ...
- HDFS Architecture
http://hadoop.apache.org/docs/r2.9.0/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html Introduction Ha ...
- HDFS要点剖析
谈到大数据,不得不提的一个名词是"HDFS".它是一种分布式文件存储系统,其系统架构图如下图所示: 从图中可以了解到的几个关键概念 元数据(MetaData) 机架(Rock) 块 ...
- hadoop(五)HDFS原理剖析
一.HDFS的工作机制 工作机制的学习主要是为加深对分布式系统的理解,以及增强遇到各种问题时的分析解决能 力,形成一定的集群运维能力PS:很多不是真正理解 hadoop 工作原理的人会常常觉得 HDF ...
- HDFS Client 设计实现解析
前面对 HDFS NameNode 和 DataNode 的架构设计实现要点做了介绍,本文对 HDFS 最后一个主要构成组件 Client 做进一步解析. 流式读取 HDFS Client 为客户端应 ...
- HDFS DataNode 设计实现解析
前文分析了 NameNode,本文进一步解析 DataNode 的设计和实现要点. 文件存储 DataNode 正如其名是负责存储文件数据的节点.HDFS 中文件的存储方式是将文件按块(block)切 ...
随机推荐
- Linux知识扩展一:执行前为什么加./
转载:https://www.cnblogs.com/fortunel/p/8663669 1 ./表示当前路径,在执行可执行文件时,linux系统会从环境变量PATH中查找该文件的路径,但因为 L ...
- Xadmin显示视图
.display显示要设置的字段 1. 自定义样式类,显示出要显示的字段,在这个类中,也可以设置对应函数. list_display=[check,"title",delete]2 ...
- AUTEL MaxiSys MS908S Pro MS908SP Diagnostic Platform
AUTEL MaxiSys MS908S Pro Description : One of the MaxiSys series devices, the MS908S Pro Diagnostic ...
- boost 编写finger服务
本篇是模仿PYTHON TWISTED写一个FINGER示例. 从最简单的链接到通过接收字符串返回不同的内容 1 最简单的链接 #include <ctime> #include < ...
- JavaScript 函数定义和调用
普通的函数定义方法: function abs(x):{ if (x >= 0){ return x; }else { return -x ; } } 两种方法是等价的 var abs = fu ...
- Release file is expired, Updates for this repository will not be applied.(资源索引文件过期问题)
将Debian下载源同步到本地之后,通过本地资源地址进行apt update操作时提示过期问题: E: Release file for http://localhost/security/dists ...
- Codeforces Round #486 (Div. 3) D. Points and Powers of Two
Codeforces Round #486 (Div. 3) D. Points and Powers of Two 题目连接: http://codeforces.com/group/T0ITBvo ...
- UML标准建模语言与应用实例
一.基本信息 标题:UML标准建模语言与应用实例 时间:2012 出版源:科技创新导报 领域分类:UML标准建模语言 面向对象 系统分析与设计 二.研究背景 问题定义:UML建模语言用图形来表现典型的 ...
- ReSharper 10.0.0.2 Ultimate 破解
文件下载地址:http://pan.baidu.com/s/1gf7l8cF 1.安装ReSharper 10.0.0.2 Ultimate 2.修改Products.json文件的FilePath, ...
- Git学习篇之git remote add origin错误
提示出错信息:fatal: remote origin already exists. 解决办法如下: 1.先输入$ git remote rm origin 2.再输入$ git remote ad ...