共计 3057 个字符,预计需要花费 8 分钟才能阅读完成。
一、概要
公司近期 Storm 清洗程序那边反应 HDFS 会出现偶发性的异常导致数据写不进 HDFS,另外一些 Spark 作业在大规模往 HDFS 灌数据时客户端会出现各种“all datanode bad..”以及服务端出现各种 timeout,值得注意的是出现这样的问题是各个 datanode 节点的负载并不高!
二、故障分析
首先,当我们在 HDFS 客户端看到各种 timeOut… 什么 waiting for reading 等信息的时候,我们第一反应是为什么在往 HDFS 写数据时组成 pipeline 的各个 datanode 接收不到上游 datanode 的 packet?这时候往往有人会说增加什么与客户端的超时时间,这个是肯定不行的(因为各个节点的负载非常低)。另外,如果出现“All datanode bad..”这种错误,我们往往会最先蹦出 2 种想法,第一:所有 datanode 都无法提供服务了,第二:dfsClient 与 hdfs 服务端的 DataXServer 线程连接但是长时间没有 packet 传输而导致 hdfs 服务端启动保护机制自动断开,最终导致。
对于现在“All datanode bad..”这种问题,我基本可以排除第二种情况。再往下看,在平台监控系统中观察 datanode 的 Thread Dump 信息以及心跳信息,发现问题了:
重现异常情况,观察所有 datanode 的 Thread Dump 和心跳情况:
这个非常恐怖,心跳达到 30s!!
更进一步分析,直接重现谷中,用 jstack -l 命令看 datanode 具体的 Thread Dump 信息发现系统高密度调用 FsDataSetImp 中 createTemporary 和 checkDirs 方法:
由于上面频发调用在粗粒度对象锁 FsDatasetImpl 作用下的方法,导致发送心跳以及 DataXceiver 线程被 blocked 掉(因为他们同样在粗粒度对象锁 FsDatasetImpl 作用下),看 Thread Dump 信息,DataNode 处理请求的 DataXceiver 线程被 blocked:
发送心跳线程被 blocked 掉:
对于发送心跳的线程被 blocked 掉,从源码的看来主要是由于 datanode 向 namenode 发送心跳需要获得所在节点的资源相关情况,心跳通过 getDfUsed,getCapacity,getAvailable,getBlockPoolUsed 等方法获得(看 FsDatasetImpl 代码):
而这几个方法又是在 FsDatasetImpl 对象锁的作用范围下,因此心跳线程被 blocked 掉,具体看下 getDfSUSEd 源码:
通过上面一轮的分析,基本可以分析故障原因:大规模往 hdfs 同时写多批文件,Datanode Thread Dump 大量 DataXceiver 和发送心跳线程被 Blocked 掉,出现心跳异常有时候达到几十秒左右,大量 DataXceiver 线程被 blocked 掉无法向各个 dfsClient 的 dataStreamer(向 datanode 发送 packet) 和 ResponseProcessor(接收 pipeline 中 datanode 的 ack) 线程提供服务和 datanode 的 BlockReceiver 线程无法正常工作,最终导致客户端出现 timeOut,或者 dfsClient 往 hdfs 写 packet 时整个 PipeLine 中的 datanode 无法响应客户端请求,进而系统内部启动 pipeline 容错,但是各个 datanode 都由于 DataXceiver 大量被 Blocked 掉无法提供服务,最后导致客户端报出“All dataNode are bad….”和服务端的 timoeut。
换句话来说这个是 HDFS 中的一个大 BUG。
这个是 Hadoop2.6 中的 bug,代码中采用了非常大粒度的对象锁(FsDatasetImpl),在大规模写操作时导致锁异常。这个 bug 出现在 2.5 和 2.6 这 2 个版本中(我们新集群用的是 2.6),目前这个 bug 已经在 2.6.1 和 2.7.0 这 2 个版本中修复。官方具体给出的 Patch 信息:
https://issues.apache.org/jira/browse/HDFS-7489
https://issues.apache.org/jira/browse/HDFS-7999
其实具体的修复方案就是将这个大粒度的对象锁分解为多个小粒度的锁,并且将 datande 向 namenode 发送心跳线程从相关联的锁中剥离。
为了进一步确认,这是 hadoop2.6 中一个 bug,我通过将测试集群升级到 2.7.1(bug 修复版本),对比 2.7.1bug 修复版本在大规模写多批文件时的 datanode 心跳线程以及 DataXceiver 线程的 blocked 以及心跳间隔情况。下面是 hadoop2.7.1 表现情况:
通过大规模往 hdfs 写多批文件,测试集群升级到 hadoop2.7.1 后,客户端没有报 timeout 和“All datanode bad…”异常,服务端也没有报 timeOut 异常。另外,通过和上文 hadoop2.6.1 中图表展示对比,发现这个 bug 在 2.7.1 得到了解决。
三、故障处理
这个故障对我们现有业务的影响大概有:
a、影响某个时间点通过 storm 写入 hdfs 的数据
b、作业提交时间点刚刚好遇到这个 hdfs 异常触发点时,会导致作业附属文件无法上传至 hdfs 最终导致作业提交失败.
c、如果 hdfs 这个异常点中时间拉长,可能会导致 MR 作业的的容错触发次数超过 3 次,最终导致作业失败。
具体处理方案:在不停机的情况下采用平滑升级至 hadoop2.7.1 版本
具体的升级步骤按照 http://hadoop.apache.org/docs/r2.6.0/hadoop-project-dist/hadoop-hdfs/HdfsRollingUpgrade.html 来就 ok。
Hadoop 如何修改 HDFS 文件存储块大小 http://www.linuxidc.com/Linux/2013-09/90100.htm
将本地文件拷到 HDFS 中 http://www.linuxidc.com/Linux/2013-05/83866.htm
从 HDFS 下载文件到本地 http://www.linuxidc.com/Linux/2012-11/74214.htm
将本地文件上传至 HDFS http://www.linuxidc.com/Linux/2012-11/74213.htm
HDFS 基本文件常用命令 http://www.linuxidc.com/Linux/2013-09/89658.htm
Hadoop 中 HDFS 和 MapReduce 节点基本简介 http://www.linuxidc.com/Linux/2013-09/89653.htm
《Hadoop 实战》中文版 + 英文文字版 + 源码【PDF】http://www.linuxidc.com/Linux/2012-10/71901.htm
Hadoop: The Definitive Guide【PDF 版】http://www.linuxidc.com/Linux/2012-01/51182.htm
更多 Hadoop 相关信息见 Hadoop 专题页面 http://www.linuxidc.com/topicnews.aspx?tid=13
本文永久更新链接地址 :http://www.linuxidc.com/Linux/2015-08/121796.htm