shoufeng

瘦风的南墙

HDFS 04 - 你能说说 HDFS 的写入和读取过程吗?

1 - HDFS 文件的写入

1.1 写入过程

1、Client 发起文件上传请求,通过 RPC 与 NameNode 建立连接,NameNode 检查 Client 是否有相应权限,以及目标文件是否已存在、父目录是否存在,向 Client 返回是否可以上传;

2、Client 从 NameNode 中获取第一个 block 的传输目的地,也就是应该传输到哪些 DataNode 上;

3、NameNode 根据配置文件中指定的备份数量及机架感知原理进行文件分配,返回可用的 DataNode 的地址,假设分别是 A、B、C;

机架感知:Hadoop 在设计时考虑到数据存储的安全与高效,数据文件在 HDFS 上默认存放三份副本。

副本的存储策略为:客户端所在 DataNode 一份,同机架内其他 DataNode 节点上一份,不同机架的某个 DataNode 节点上一份。

更多请参考:HDFS 03 - HDFS 的几大机制:副本机制、机架感知机制、负载均衡机制

4、Client 先向 DataNode A 上传数据(也是一个 RPC 调用,建立 pipeline ),A 收到请求会继续调用 B,然后 B 调用 C,建立完成所有的 pipeline 之后,逐级返回 Client;

5、Client 开始向 DataNode A 上传第一个 block,以 packet 为单位(默认是64K),A 收到一个 packet 就会传给 B,B 传给 C,A 每传一个 packet 会放入一个 ack 队列等待应答;

6、数据被分割成一个个的 packet 数据包在 pipeline 上依次传输,在 pipeline 反方向上逐个发送 ack(操作是否成功的应答),最终由 pipeline 中第一个 DataNode 节点 A 将 pipelineack 发送给 Client;

7、当一个 block 传输完成后,Client 再次请求 NameNode 上传第二个 block ,继续上述 (4) - (7) 步骤。

1.2 写入异常时的处理

当数据写入失败时,HDFS 会作出以下反应:

首先管道会被关闭,任何在确认通知队列中的文件包都会被添加到数据队列的前端,以确保故障节点下游的 DataNode 不会漏掉任何一个数据包。

为存储在另一个正常的 DataNode 的当前数据块指定一个新的标识,并将标识传送给 NameNode,以便 故障 DataNode 在恢复后可以删除存储的部分数据块

删除故障 DataNode 的 pipeline 连接,基于两个正常的 DataNode 构建新的 pipeline,剩余的数据块写入新的 pipeline 中。

NameNode 在注意到副本个数不足时,会在其他 DataNode 上创建一个新的副本。后续的数据块就能正常的接受处理。

1.3 写入的一致性

  • 新建一个文件后,HDFS 的命名空间中立即可见;
  • 写入文件的内容不保证立即可见(即使数据流已经调用 flush() 方法刷新并存储);
  • 当前正在写入的块对其他 Reader 不可见;
  • 调用 hflush() 方法后,数据被写入 DataNode 的内存中,可保证对所有 Reader 可见;
  • 调用 hsync() 方法后,数据就会被写到磁盘上;
  • 如果没有调用 hflush()hsync(),客户端在故障的情况下可能丢失正在写入的数据块。

2 - HDFS 文件的读取

2.1 读取过程

1、Client 向 NameNode 发起 RPC 请求,来确定请求文件 block 所在的位置;

2、NameNode 会视情况返回文件的部分或者全部 block 列表:对每个 block,NameNode 都会返回含有该 block 副本的 DataNode 地址 —— 按照集群拓扑结构得出 DataNode 与客户端的距离,然后进行排序,排序规则是:

网络拓扑结构中距离 Client 近的靠前 —— 网络拓扑需要根据数据中心、机架、数据节点等因素手动设置;

心跳机制中超时汇报的 DataNnode 状态为 STALE 的排靠后。

3、Client 选取排序靠前的 DataNode 来读取 block,如果客户端本身就是 DataNode,就会从本地直接获取数据(短路读取特性);

4、底层本质是建立 Socket Stream(FSDataInputStream),重复调用父类 DataInputStream 的 read 方法,直到这个 block 上的数据读取完毕;

5、当读完列表的 block 后,若文件读取还没有结束,客户端会继续向 NameNode 获取下一批的 block 列表;

DataInputStream#read() 方法是并行读取 block 信息,仍然是以 packet 为单位(默认是64K)在 pipeline 上依次传输;

6、读取完所有的 block 后,客户端会将它们合并成一个完整的文件。

【注意】NameNode 只是返回 Client 请求的 block 的 DataNode 地址,并不会返回 block 的具体数据。

2.2 读取异常时的处理

1)如果客户端和所连接的 DataNode 在读取数据时出现故障,那客户端就会去尝试连接存储这个 block 的下一个最近的 DataNode,同时它会 记录这个发生故障的 DataNode,以避免后续再次连到这个有问题的 DataNode 节点。

2)客户端每读完一个 block 都会进行 checksum 验证,如果从某个 DataNode 上读取到的 block 是损坏的,客户端会通知 NameNode 更新该 block 的相关信息,然后从下一个拥有该 block 副本的 DataNode 继续读取文件。


版权声明

作者:瘦风(https://healchow.com)

出处:博客园-瘦风的南墙(https://www.cnblogs.com/shoufeng)

感谢阅读,公众号 「瘦风的南墙」 ,手机端阅读更佳,还有其他福利和心得输出,欢迎扫码关注🤝

本文版权归博主所有,欢迎转载,但 [必须在页面明显位置标明原文链接],否则博主保留追究相关人士法律责任的权利。

posted @ 2021-03-01 08:41  瘦风  阅读(746)  评论(0编辑  收藏  举报