Hdfs写流程异常处理

2,124 views
1,986 views

Published on

梳理hdfs写

Published in: Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,124
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
81
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Hdfs写流程异常处理

  1. 1. HDFS 写异常分析文档版本控制 文档版本号 日期 作者 审核人 说明 V0.1 刘景龙
  2. 2. 目录1. 目的.................................................................................................................................................. 32. DFSClient 分析 ................................................................................................................................ 3 2.1 pipeline 建立........................................................................................................................... 6 2.2 数据传输 ................................................................................................................................. 6 2.3 close() ...................................................................................................................................... 83. DataNode 分析 ............................................................................................................................... 8 3.1 错误分析 ................................................................................................................................. 8 3.2 出错调查 TIPS ......................................................................................................................... 114. 常见日志意义 .............................................................................................. 错误!未定义书签。 4.1 pipeline 建立......................................................................................................................... 11 4.2 数据传输 ............................................................................................................................... 11
  3. 3. 1. 目的 本文档意在明确写流程过程中操作步骤以及各步中可能出现的异常。各步出现的异常请见文档中的流程图。2. DFSClient 分析 DFSClient DataStreamer ResponseProcessor Namenode DataNode1 DataNode2 DataNode3 write()将packet塞到dataQueue 0.从data queue中取出packet 1. addBlock() 2. 建立socket连接 3. 发送WRITE_BLOCK报文 4. new BlockReceiver 5. 建立socket连接 6. 发送WRITE_BLOCK报文 7. new BlockReceiver 8. 建立socket连接 9. 发送WRITE_BLOCK报文 10. new BlockReceiver 11. first bad link ack 12. first bad link ack 13. first bad link ack 启动ResponseProcessor 将packet从dataQueue中移出,加入到ackQueue 14. 向socket中写packet 15. 向socket中写packet 16. 向socket中写packet 17. 接收packet,flush到本地磁盘 18. packet ack 19. 接收packet,flush到本地磁盘 20. packet ack 21. 接收packet,flush到本地磁盘 22. packet ack 23. 收到最后一个packet的ack stop掉ResponseProcessor线程 close掉打开的流 close掉打开的流 24. close掉使用的流 close掉打开的流 以上为 HDFS 写流程图: 1. Rpc 调用 namenode 的 addBlock() 方法来获得新的 block 的 3 个副本位置。 2. DFSClient 建立到 primary datanode 的 socket 连接。 3. DFSClient 向 primary datanode 发送 WRITE_BLOCK 报文。
  4. 4. 4. Primary datanode 收到 WRITE_BLOCK 报文后,new BlockReceiver 对象,并创建 block 和 meta 文件。 5. primary datanode 建立到第二个 datanode 的 socket 连接。 6. primary datanode 向第二个 datanode 发送 WRITE_BLOCK 报文。 7. 第二个 datanode 收到 WRITE_BLOCK 报文后,new BlockReceiver 对象。并创建 block 和 meta 文件。 8. 第二个 datanode 建立到最后一个 datanode 的 socket 连接。 9. 第二个 datanode 向最后一个 datanode 发送 WRITE_BLOCK 报文。 10. 最后一个 datanode 收到 WRITE_BLOCK 报文后,new BlockReceiver 对象并创建 block 和 meta 文件。 11. 最后一个 datanode 返回 ack 给第二个 datanode。 12. 第二个 datanode 返回 ack 给 primary datanode。 13. primary datanode 返回 ack 给 DFSClient。 14. DFSClient 向 primary datanode 写 packet。 15. primary datanode 向第二个 datanode 写 packet 16. 第二个 datanode 向最后一个 datanode 写 packet。 17. 最后一个 datanode 写该 packet 数据到 block/meta 文件。 18. 最后一个 datanode 返给 ack 给第二个 datanode。 19. 第二个 datanode 写该 packet 数据到 block/meta 文件。 20. 第二个 datanode 返给 ack 给 primary datanode。 21. primary datanode 写该 packet 数据到 block/meta 文件。 22. primary datanode 返给 ack 给 DFSClient。注:对于非最后 datanode 来讲,发送过程中把数据写到本地磁盘(block/meta)同向上一个datanode 发送 ack 并无绝对先后关系。datanode 中发送数据和收 ack 是在 BlockReceiver 和PacketResponser 这 2 个线程中完成。BlockReceiver 负责收到数据,发送给下一个 datanode,并将数据写本地文件。PacketResponser 负责收到后一个 datanode 的 ack,并将该 ack 转发给上一个 datanode。当没有数据传输时,pipeline 中各 datanode 通过心跳对连接 keepalive。BlockReceiver 线程处理数据按照以下顺序: 1. 收到 packet 2. 把 packet 写给下一个 datanode 3. 将 packet flush 到 datanode 本地 block/meta 文件 4. 将 packet 加入到 ack queue 中最后一个 datanode 和其他 datanode 中的 PacketResponser 线程以不同方式处理 ack。 对于最后一个 datanode 处理顺序如下: 1. 向上一个 datanode 发送 heartbeat (heartbeat 间隔为 dfs.socket.timeout/2, 默 认为 30s) 2. 等待 ack queue 中对应 seq number 3. 如果是最后一个包,finalize block 4. 向上一个 datanode 发送 ack 对于其他 datanode 处理顺序如下: 1. 收到下一个 datanode 的 ack(即使收不到下一个 datanode 的 ack,也会执行第 2 步) 2. 等待 ack queue 中对应 seq number
  5. 5. 3. 将 ack 发给上一个 datanodePacketResponser 线程的第二步等 BlockReceiver 线程中的第 4 步完成,才可继续进行下列步骤。 NotReplicationYet 重试,dfs.client.block.write.locateFollowBlock.retries, 默认为5。重试间隔初始为400ms,后一次是前一个的2倍 Init() addBlock() Rpc调用namenode addblock时,如果该文件的倒数第二个块 副本小于最小副本数(dfs.replication.min设置,默认为1) 则报NotReplicationYetException DFSClient建立到primary datanode的socket连接 重试次数通过dfs.client.block.write.retries设置,默认为3次 N Pipeline建立 DFSClient向primary datanode发送WRITE_BLOCK 出现此异常的原因为DFSC 报文 lient到primary datanode之间网络出现问题 primary datanode返回ack给 出现异常 DFSClient Close socket 重试未达到最大重试 次数 AbandonBlock() Success ? 重试间隔6s 成功 重试达到最大重试 次数 启动Response Processor线程 将packet从dataqueue 中移出,放到ack Y queue中 到primary DFSClient 向primary datanode的连 datanode 写packet 接是否存在 数据传输 最后一个块? Y 出现异常 processDataError() 出现异常 N 写EOF Socket flush() 出现异常 出现此异常的原因为 DFSClient到primary 如果是最后块则close流 datanode之间网络 ,和ResponseProceessor end 出现问题 线程写的过程可以分为 3 个阶段 pipeline 建立,数据传输,close()。
  6. 6. 2.1 pipeline 建立 (1) 在pipeline建立的过程中,DFSClient首先rpc调用namenode的addblock(),向namenode 申请add一个block,并返回该block的三个副本的位置。如果addblock()失败,则进行 重试,重试次数为dfs.client.block.write.locateFollowBlock.retries, 默认为5。重试间隔 初始为400ms,后一次是前一次的2倍。如果重试达到最大重试次数,则抛出异常, 进入(3),如果成功,则顺序执行步骤(2)。 (2) 如果addBlock()正常返回3个datanode位置信息,则执行以下几步: a) 建立到primary datanode的socket连接 b) 向primary datanode发送WRITE_BLOCK命令 c) 等待primary datanode的ack应答 如果以上各步中有一步出错,则进入步骤(3)。如果以上3步都成功,则pipeline 建立成功。 (3) 当(1), (2)中出现IOException的时候,会丢弃当前Block,进行重试,重新 addBlock()。该重试次数通过重试次数通过dfs.client.block.write.retries设置,默认为3 次2.2 数据传输 DFSClient 的数据发送过程比较简单。 就是 DataStreamer 线程从 data queue 中取出 packet,将其放入 ack queue,然后将该 packet 发给 primary datanode。当数据传输过程中,如果出现IOException 错误,则会触发出错处理机制。出错处理集中在 processDataError()进行。 出错处理的过程如下: (1) 由于需要数据重发,将 ack queue 中 的 packet 重新塞回到 data queue 中。 (2) 将之前出错的 datanode 剔除。 (3) 按照 host:port 的字典序进行排序,将最小的 datanode 作为新的 primary datanode (4) 对 primary datanode 进行 block recovery,如果失败,则调用 processDataError()返回, 由外层循环进行重试。 (5) 如果成功,则建立到 primary datanode 的 socket 连接。 (6) 向 primary datanode 发送 WRITE_BLOCK 报文。 以上各步中出现错误,则 close 掉 socket 之后,函数返回,有外层循环进行重试。 当一次数据传输出现错误的时候,会调用 processDataError() 尝试进行 block recovery。举例:我们有 3 个副本分别在 127.0.0.1, 127.0.0.2, 127.0.0.3 3 个 datanode 上。(1) 初始状态,node 列表 127.0.0.3 127.0.0.1 127.0.0.2(2) 127.0.0.1 出错,将 127.0.0.1 踢出,其余 datanode 按照 hostname:port 排序,并以第一 个 datanode 为 primary(即 127.0.0.2 为 primary) ,对 127.0.0.2 重试 6 次。 127.0.0.2 127.0.0.3(3) 127.0.0.2 出错,将 127.0.0.2 踢出,其余 datanode 按照 hostname:port 排序,并以第一个 datanode 为 primary(即 127.0.0.3 为 primary) ,对 127.0.0.3 重试 6 次。
  7. 7. 127.0.0.1 127.0.0.3 (4) 127.0.0.1 出错,将 127.0.0.1 踢出,其余 datanode 按照 hostname:port 排序,并以 第一个 datanode 为 primary(即 127.0.0.3 为 primary) ,对 127.0.0.3 重试 6 次。 127.0.0.3(5) 127.0.0.3 出错,node list 为空。不再进行 block recovery。 当一个 block 的其他副本为 3 的时候,进行 block recovery 次数,小于 3 * 6 +1= 19 次, 则 block recovery 成功。如果等于 19 次,则该 block recovery 失败。 processDataError() close掉流 把ack queue中的packet加入到data queue中 踢出出错datanode 将剩下的datanode按照host :port的字典序排序,以最 小的dn作为primary datanode 对primary datanode进行 失败 Return block recovery 成功 DFSClient建立到primary datanode的socket连接 失败 DFSClient向primary datanode发送WRITE_BLOCK 出现此异常的原因为DFS 报文 Client到primary datanode之间网络 primary 出现问题 datanode返回ack给 throw IOException DFSClient Close socket Success ? 成功 启动Response Processor线程
  8. 8. 2.3 Close() 该异常原因为DFSClient到 primary datanode网络连接 出现问题 将buffer中的数据 flushBuffer() Throw IOEXception 写入到socket中 该函数仅当该DFSClient已经 被close时,才会报出 不停的等待,直到 收到全部的ack flushInternal() Throw IOEXception 异常退出 关闭DataStreamer和 closeThreads() Throw IOEXception 当DataStreamer和 ResponseProcessor线程join ResponserProccesor线程 的时候,如果被interrupt, 则会报出该异常 Throw IOEXception Close连接和打开的流 Close连接异常 如果complete不成功 Throw IOEXception Namenode.Complete() Complete不成功:complete 返回状态为STILL_WAITING 成功:返回状态 COMPLETE_SUCCESS 异常:其他状态,包括安全模 式,或者写editlog出现异常 上图描述了 DFSClient close() 的过程,不难发现,当 flushBuffer(), flushInternal(), closeThreads(), close 连接抛出 IOException 时,close 操作直接将 IOException 抛出。而 Complete()操作一直忙等,直到 complete 成功为止,在 complete 操作中,namenode 会将之前持有的 lease 释放。 当 close 异常退出的时候,会有 2 种资源未被 client 处理。 a) 异常退出时,网络连接未能及时 close (需要修复该 issue) b) 异常退出时,lease 未能释放。 在 namenode 维护 2 个 lease limit。Soft limit 和 hard limit。 当 lease 超过 soft limit(1 分钟),另一个 DFSClient append 该文件时会触 发 lease recovery,且该次 append 失败。但下次重试 append 即可成功。 当 lease 超过 hard limit(1 小时) ,则 namenode 触发 lease recovery,释放 该 lease。3. DataNode 分析3.1 错误分析 Datanode 的行为分为 pipeline 建立和数据传输 2 部分。图 3-1 中描述的是 datanode 在建立 pipeline 过程中的流程。其中阴影块 receive block() 为数据传输逻辑,在图 3-2 中详细描述。
  9. 9. 收到IOException,重试收到DFSClient(或上一个datanode)的connect, 出错原因:当前datanode到 建立连接 上一个datanode网络出现异 常 记录日志, 收到WRITE_BLOCK命令 Throw IOException 异常退出 关闭连接New BlockReceiver对象 Throw IOException 出错原因:datanode本地操(创建block/meta文件) 作会导致IOEXception,有可 能是磁盘IO压力造成,需要详 细调查 建立到下一个 Close 掉连接 datanode的连接 Throw IOException向下一个datanode发送 出错原因:当前datanode到下一 个datanode网络出现异常 WRITE_BLOCK命令等待下一个datanode返回 Throw IOException的first bad link的ack 成功 出错原因:当前datanode到 向前一个 上一个datanode网络出现异 常 datanode发送 ack Receive block 参见图3-2 success 图 3-1 datanode 在建立 pipeline 流程
  10. 10. 1.启动PacketRespond er线程 该函数出现IOException的原 因是上一个datanode到本 datanode的网络连接异常 2.readNextPacke t 如果是此种异常,则需要进一 步定位 3.setBlockPosition 该函数出现IOException的原 因是本datanode到下一个 datanode的网络连接异常 4.将读入的packet数 据写入mirror out 5.如果该datanode为pipeline中 最后一个verifyChunks 还有后续包 Ioexception Finalized ? N Y 6.将数据写到磁盘。 7.CheckData 包括写block和meta nodeError IOException 该函数出现IOException的原 因是本datanode的磁盘异常 8.将整个packet flush。包括block和meta 9.将pkt放入response队列中,由 PacketResponder线程发出应答 10.向mirror out写EOF Throw IOException 12.cleanupBlock 该函数出现IOException的原 11.关闭PacketResponder线程 因是本datanode到下一个 datanode的网络连接异常 图 3-2 datanode 在数据传输流程 不难发现,在建立 pipeline 的过程中有 3 种异常。1) Datanode 作为 socket server 时, accept 到 DFSClient 来的请求时, 在 如果出现异常,则忽略异常,datanode 继续 accept 连接。2) 当 datanode 向下一个 datanode 建立连接, 发送 WRITE_BLOCK 指令或者在接受 firstbad link ack 时出现错误时, datanode 会 close 掉同下一个 datanode 的网络连接并向上一个 datanode(或者是 DFSClient)发送 ack 并 receive block 接受数据。3) 其他情况出现 IOException 都会导致该该 DataXeceiver 线程异常退出。导致这种异常的原因有二: a) 同上一个 datanode 交互。包括接收 WRITE_BLOCK 命令和向上一个 datanode 发送 ack。此时,可能是由于上一个 datanode 到该 datanode 的网络连接繁忙。 可检查事发时刻网络状况。
  11. 11. b) Create 本地 block 和 meta 文件。 出现此类错误时, 有可能是由于创建本地 block 和 meta 文件时出错,具体情况可检查 datanode 日志。 以上问题的定位,可通过日志中打出的 stack 信息协助定位。 Datanode 中数据传输在 receive block 中完成。各步网络及磁盘操作中皆可能出现 IOException。在 receive block 中,并无重试机制。出现错误会导致 DataXeceiver 线程异 常退出。数据传输过程中的网络和磁盘繁忙皆会导致此类错误。3.2 出错调查 tips 当出现写异常时,可以通过查看写错误时对应的 meta 及 block 文件。 如果文件已经存在,且不为空,则意味着 pipeline 已经建立。错误出现在数据传输 过程中。可对照图 3-2 协助问题定位。 如果 meta/block 文件不存在, 有可能是由于 datanode 重启,或者 new BlockReceiver 对象,未能成功,需要进一步定位。 如果 meta/block 文件为空,则可证明 new BlockReceiver 对象成功,可检查下一个 datanode 协助定位问题。4. 常见日志意义4.1 pipeline 建立case1 :DFSClient 中报 warn 日志 NotReplicatedYetException sleeping <路径> retries left,无该日志 addBlock ()一次成功小于 4 次 addBlock()通过重试成功等于 4 次后紧跟 warn 日志 ”DataStreamer addBlock() 失败Exception:”等于 4 次后紧跟 warn 日志 ”DataStreamer addBlock() 在最后一次重试成功Exception:”处理方式:如果 addBlock()失败,则需要查看当时 namenode 的日志。Case 2: 出现 info 日志 “Excluding node:”, 后紧接着 “Abandoning block” 代表建立 pipeline时出错,进行 abandon block,然后重试。处理方式:如果重试次数小于 5,则可忽略。Case 3:出现 info 日志“Exception in createBlockOutputStream” 后有“Bad connect ack withfirstBadLink” 代表建立 pipeline 的时候, 接收日志中指定的 datanode 的 ack 时, socket timeout。处理方式:检查日志中指定 datanode 的负载状况,有可能是网络繁忙造成。4.2 数据传输Case 4:出现warn日志"Error Recovery for block " + block + " failed " + " because recovery from primary datanode " + primaryNode + " failed " + recoveryErrorCount + " times. " + " Pipeline was " 代表日志中指定的 datanode 出现 block recovery 失败处理方式:查看指定 datanode 的日志和监控有可能是由于当时网络繁忙或者 datanode 其他原因造成出错。当文件副本数位 3 时,Error Recovery for block 出现 19 次,则表示该 blockrecovery 失败。应查看对应 datanode 的日志,以进一步定位问题。Case 5: 出现 info 日志“Exception in receiveBlock for block”日志时,说明是数据传输过程中出现异常。需要根据该日志中报出的 block id 和异常栈协助定位。

×