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 Federation简介
背景 熟悉大数据的人应该都知道,HDFS 是一个分布式文件系统,它是基于谷歌的 GFS 思路实现的开源系统,它的设计目的就是提供一个高度容错性和高吞吐量的海量数据存储解决方案。在经典的 HDFS 架构中有2个 NameNode 和多个 DataNode 的,如下: 从上面可以看出 HDFS 的架构其实大致可以分为两层: Namespace:由目录,文件和数据块组成,支持常见的文件系统操作,例如创建,删除,修改和列出文件和目录。
|
5月前
|
存储 分布式计算 Hadoop
Hadoop Distributed File System (HDFS): 概念、功能点及实战
【6月更文挑战第12天】Hadoop Distributed File System (HDFS) 是 Hadoop 生态系统中的核心组件之一。它设计用于在大规模集群环境中存储和管理海量数据,提供高吞吐量的数据访问和容错能力。
636 4
|
6月前
|
存储 分布式计算 资源调度
|
存储 分布式计算 Hadoop
HDFS组成架构
HDFS组成架构
|
存储 分布式计算 安全
HDFS的特点、三个服务、架构
一、高可靠性:hadoop一般都在成千的计算机集群之上,且可以搭建hadoop的高可靠集群,及内部容错功能优秀。 二、高扩展性:hadoop是在可用的计算机集簇间分配数据并完成计算任务的,这些集簇可以方便地扩展到数以千计的节点中。 三、高效性:hadoop能够在节点之间动态地移动数据,并保证各个节点的动态平衡,因此处理速度非常快。 四、高容错性:Hadoop能够自动保存数据的多个副本,并且能够自动将失败的任务重新分配。 #### 缺点:
388 0
HDFS的特点、三个服务、架构
|
消息中间件 分布式计算 大数据
Structured_Sink_HDFS | 学习笔记
快速学习 Structured_Sink_HDFS
Structured_Sink_HDFS | 学习笔记
|
存储 分布式计算 监控
HDFS1.0 与HDFS2.x 架构理解
HDFS1.0 与HDFS2.x 架构理解
321 0
HDFS1.0 与HDFS2.x 架构理解
|
存储 机器学习/深度学习 SQL
Hadoop基础-03-HDFS(Hadoop Distributed File System)基本概念
Hadoop基础-03-HDFS(Hadoop Distributed File System)基本概念 14
167 0
Hadoop基础-03-HDFS(Hadoop Distributed File System)基本概念
|
存储 缓存 分布式计算
HDFS 架构
前段时间搭建了一套Hadoop集群的测试环境,因为服务器故障,废了。这几天闲来无事,想着把Storm用Yarn管理起来,于是再来一遍,同时也梳理下Hadoop组件中的一些概念。所谓书读百遍其义自见,不熟的系统多搭几遍,总会熟悉了,也就是所谓的刻意练习吧。
214 0
HDFS 架构