在前面的文章 《HDFS DataNode 设计实现解析》中咱们对文件操做进行了描述,但并未展开讲述其中涉及的异常错误处理与恢复机制。本文将深刻探讨 HDFS 文件操做涉及的错误处理与恢复过程。html
读文件可能发生的异常有两种:算法
HDFS 的文件块多副本分散存储机制保障了数据存储的可靠性,对于第一种状况 DataNode 挂了只须要失败转移到其余副本所在的 DataNode 继续读取,而对于第二种状况读取到的文件数据块若校验失败可认定为损坏,依然能够转移到读取其余无缺的副本,并向 NameNode 汇报该文件 block 损坏,后续处理由 NameNode 通知 DataNode 删除损坏文件 block,并根据无缺的副原本复制一份新的文件 block 副本。apache
由于读文件不涉及数据的改变,因此处理起来相对简单,恢复机制的透明性和易用性都很是好。微信
以前的文章中对写文件的过程作了描述,这个过程当中可能发生多种不一样的错误异常对应着不一样的处理方式。先看看有哪些可能的异常?markdown
可能的异常模式以下所列:网络
对于以上所列的异常模式,都有分别对应的恢复模式。app
当 Client 在写入过程当中,本身挂了。因为 Client 在写文件以前须要向 NameNode 申请该文件的租约(lease),只有持有租约才容许写入,并且租约须要按期续约。因此当 Client 挂了后租约会超时,HDFS 在超时后会释放该文件的租约并关闭该文件,避免文件一直被这个挂掉的 Client 独占致使其余人不能写入。这个过程称为 lease recovery。分布式
在发起 lease recovery 时,若多个文件 block 副本在多个 DataNodes 上处于不一致的状态,首先须要将其恢复到一致长度的状态。这个过程称为 block recovery。 这个过程只能在 lease recovery 过程当中发起。ide
当 Client 在写入过程当中,有 DataNode 挂了。写入过程不会马上终止(若是马上终止,易用性和可用性都太不友好),取而代之 HDFS 尝试从流水线中摘除挂了的 DataNode 并恢复写入,这个过程称为 pipeline recovery。oop
当 Client 在写入过程当中,NameNode 挂了。这里的前提是已经开始写入了,因此 NameNode 已经完成了对 DataNode 的分配,若一开始 NameNode 就挂了,整个 HDFS 是不可用的因此也没法开始写入。流水线写入过程当中,当一个 block 写完后需向 NameNode 报告其状态,这时 NameNode 挂了,状态报告失败,但不影响 DataNode 的流线工做,数据先被保存下来,但最后一步 Client 写完向 NameNode 请求关闭文件时会出错,因为 NameNode 的单点特性,因此没法自动恢复,需人工介入恢复。
上面先简单介绍了对应异常的恢复模式,详细过程后文再描述。在介绍详细恢复过程前,须要了解文件数据状态的概念。由于写文件过程当中异常和恢复会对数据状态产生影响,咱们知道 HDFS 文件至少由 1 个或多个 block 构成,所以每一个 block 都有其相应的状态,因为文件的元数据在 NameNode 中管理而文件数据自己在 DataNode 中管理,为了区分文件 block 分别在 NameNode 和 DataNode 上下文语境中的区别,下面咱们会用 replica(副本)特指在 DataNode 中的 block,而 block 则限定为在 NameNode 中的文件块元数据信息。在这个语义限定下 NameNode 中的 block 实际对应 DataNodes 上的多个 replicas,它们分别有不一样的数据状态。咱们先看看 replica 和 block 分别在 DataNode 和 NameNode 中都存在哪些状态?
Replica 在 DataNode 中存在的状态列表以下:
DataNode 会持久化存储 replica 的状态,每一个数据目录都包含了三个子目录:
FINALIZED
状态 replicas。TEMPORARY
状态的 replicas。RBW
、RWR
和 RUR
三种状态的 relicas,从该目录下加载的 replicas 默认都处于 RWR
状态。从目录看出实际上只持久化了三种状态,而在内存中则有五种状态,从下面的 replica 状态变迁图也能够看出这点。
咱们从 Init
开始简单描述下 replica 的状态变迁图。
Init
出发,一个新建立的 replica 初始化为两种状态: RBW
。TEMPORARY
。RBW
出发,有三种状况: FINALIZED
状态。RWR
状态,重启期间数据可能过期了,能够被丢弃。RUR
状态。TEMPORARY
出发,有两种状况: FINALIZED
状态。RWR
出发,有两种状况: RBW
状态,由于持久化目录 rbw
包含了三种状态,重启后又回到 RWR
状态。RUR
状态。RUR
出发,有两种状况: RBW
状态,重启后只会回到 RWR
状态,看是否还有必要参与恢复仍是过期直接被丢弃。FINALIZED
状态。FINALIZED
出发,有两种状况: RBW
。RUR
状态。接下咱们再看看 NameNode 上 block 的状态有哪些以及时如何变化的。
Block 在 NameNode 中存在的状态列表以下:
UNDER_CONSTRUCTION
状态下 block 在 block recovery 过程开始后会变动为该状态。FINALIZED
状态的 replica 数量少于最小副本数要求。FINALIZED
状态的 replica 数量达到最小副本数要求后,则切换到该状态。只有当文件的全部 block 处于该状态才可被关闭。NameNode 不会持久化存储这些状态,一旦 NameNode 发生重启,它将全部打开文件的最后一个 block 设置为 UNDER_CONSTRUCTION
状态,其余则所有设置为 COMPLETE
状态。
下图展现了 block 的状态变化过程。
咱们仍是从 Init
开始简单描述下 block 的状态变迁图。
Init
出发,只有当 Client 新建或追加文件写入时新建立的 block 处于 UNDER_CONSTRUCTION
状态。UNDER_CONSTRUCTION
出发,有三种状况: FINALIZED
状态的 replica 数量少于最小副本数要求,则切换到 COMMITTED
状态, FINALIZED
状态的 replica 数量达到最小副本数要求,则切换到 COMPLETE
状态UNDER_RECOVERY
。UNDER_RECOVERY
,有三种状况: COMPLETE
。UNDER_CONSTRUCTION
状态。COMMITTED
出发,有两种状况: FINALIZED
状态的 replica 数量达到最小副本数要求或者文件被强制关闭或者 NameNode 重启且不是最后一个 block, COMPLETE
状态。UNDER_CONSTRUCTION
状态。COMPLETE
出发,只有在 NameNode 发生重启,其打开文件的最后一个 block 会恢复成 UNDER_CONSTRUCTION
状态。 理解了 block 和 replica 的状态及其变化过程,咱们就能够进一步详细分析上述简要说起的几种自动恢复模式。
前面讲了 lease recovery 的目的是当 Client 在写入过程当中挂了后,通过必定的超时时间后,收回租约并关闭文件。但在收回租约关闭文件前,须要确保文件 block 的多个副本数据一致(分布式环境下不少异常状况均可能致使多个数据节点副本不一致),若不一致就会引入 block recovery 过程进行恢复。下面是整个恢复处理流程的简要算法描述:
其中 3~6 步就属于 block recovery 的处理过程,这里有个疑问为何在多个副本中选择最小长度做为最终更新一致的标准?想一想写入流水线过程,若是 Client 挂掉致使写入中断后,对于流水线上的多个 DataNode 收到的数据在正常状况下应该是一致的。但在异常状况下,排在首位的收到的数据理论上最多,末位的最少,因为数据接收的确认是从末位按反方向传递到首位再到 Client 端。因此排在末位的 DataNode 上存储的数据都是实际已被确认的数据,而它上面的数据实际在不一致的状况也是最少的,因此算法里选择多个节点上最小的数据长度为标准来同步到一致状态。
如上图所示,pipeline 写入包括三个阶段:
FINALIZED
状态,并向 NameNode 发送 block 报告。NameNode 将根据报告的 FINALIZED
状态的 replica 数量是否达到最小副本要求来改变相应 block 状态为 COMPLETE
。Pipeline recovery 能够发生在这三个阶段中的任意一个,只要在写入过程当中一个或多个 DataNode 遭遇网络或自身故障。咱们来分别分析下。
在 pipeline 准备阶段发生错误,分两种状况:
到了 close 阶段才出错,实际数据已经所有写入了 DataNodes 中,因此影响很小了。Client 依然根据剩下正常的 DataNode 重建 pipeline,让剩下的 DataNode 继续完成 close 阶段须要作的工做。
以上就是 pipeline recovery 三个阶段的处理过程,这里还有点小小的细节可说。
当 pipeline 中一个 DataNode 挂了,Client 重建 pipeline 时是能够移除挂了的 DataNode,也可使用新的 DataNode 来替换。这里有策略是可配置的,称为 DataNode Replacement Policy upon Failure,包括下面几种状况:
本文讲述了 HDFS 异常处理与恢复的处理流程和细节,它确保 HDFS 在面对网络和节点错误的状况下保证数据写入的持久性和一致性。读完本篇相信你会对 HDFS 内部的一些设计和工做状态有更深的认识,本文特意抽象的描述了一些涉及具体算法的部分。由于 HDFS 做为一个开源软件还在不断发展演变,具体算法代码实现总会变化,但设计理念和 design for failure 的设计思考要点的持续性要长久的多,会更具参考价值。若是你还想对某些特定细节的实现作进一步的了解,只能去深刻阅读对应部分的代码,这没法经过阅读文档或相关文章来得到,这也是代码开源的价值所在。
[1] Hadoop Documentation. HDFS Architecture.
[2] Robert Chansler, Hairong Kuang, Sanjay Radia, Konstantin Shvachko, and Suresh Srinivas. The Hadoop Distributed File System
[3] Tom White. Hadoop: The Definitive Guide. O’Reilly Media(2012-05), pp 94-96
[4] Yongjun Zhang. Understanding HDFS Recovery Processes
[5] Hairong
Kuang,
Konstantin
Shvachko,
Nicholas
Sze,
Sanjay
Radia,
Robert
Chansler
, Yahoo!
HDFS
team
Design Specification: Append/Hflush/Read
Design
[6] HDFSteam. Design Specification: HDFS Append and Truncates
下面是我本身开的一个微信公众号 [瞬息之间],除了写技术的文章、还有产品的、行业和人生的思考,但愿能和更多走在这条路上同行者交流,有兴趣可关注一下,谢谢。