剖析HDFS的文件写入

客户端通过对DistributedFileSystem 对象调用create()来新建文件(步骤1)。DistributedFileSystem对namenode 创建一个RPC调用,在文件系统的命名空间中新建一个文件,此时该文件中还没有相应的数据块(步骤2)。namenode执行各种不同的检查以确保这个文件不存在以及客户端有新建该文件的权限。如果这些检查均通过,namenode就会为创建新文件记录一条记录,如果检查未通过,会导致文件创建失败并向客户端抛出一个IOException。DistributedFileSystem向客户端返回一个FSDataOutputStream对象,由此客户端可以开始写入数据。就像读取事件一样,FSDataOutputStream封装一个DFSOutputStream对象,该对象负责处理datanode和namenode之间的通信。

   
      图-客户端将数据写入HDFS

在客户端写入数据时(步骤3), DFSOutputStream将它分成一个个的数据包,并写入内部队列,称为“数据队列”(data queue)。 DataStreamer处理数据队列,它的责任是挑选出适合存储数据复本的一组datanode,并据此来要求namenode分配新的数据块。这一组 datanode 构成一个管线——我们假设复本数为3,所以管线中有3个节点。DataStreamer将数据包流式传输到管线中第1个datanode, 该datanode存储数据包并将它发送到管线中的第2个datanode。同样,第2个datanode存储该数据包并且发送给管线中的第3个(也是最后一个)datanode (步骤4)。
DFSOutputStream也维护着一个内部数据包队列来等待datanode的收到确认回执,称为“确认队列”(ack queue)。收到管道中所有datanode 确认信息后,该数据包才会从确认队列删除(步骤5)。

     如果任何datanode 在数据写人期间发生故障,则执行以下操作(对写人数据的客户端是透明的)。首先关闭管线,确认把队列中的所有数据包都添加回数据队列的最前端,以确保故障节点下游的datanode 不会漏掉任何一个数据包。为存储在另一正常datanode的当前数据块指定一- 个新的标识,并将该标识传送给namenode, 以便故障datanode在恢复后可以删除存储的部分数据块。从管线中删除故障datanode,基于两个正常datanode 构建一条新管线。余下的数据块写入管线中正常的datanode。namenode 注意到块复本量不足时,会在另一个节点上创建一个新的复本。后续的数据块继续正常接受处理。

     在一个块被写入期间可能会有多个datanode同时发生故障,但非常少见。只要写入了dfs.namenode.replication.min的复本数(默认为1),写操作就会成功,并且这个块可以在集群中异步复制,直到达到其目标复本数(dfs.replication的默认值为3)。

      客户端完成数据的写入后,对数据流调用close()方法(步骤6)。该操作将剩氽的所有数据包写人datanode 管线,并在联系到namenode告知其文件写人完成之前,等待确认(步骤7)。namenode已经知道文件由哪些块组成(因为Datastreamer请求分配数据块),所以它在返回成功前只需要等待数据块进行最小量的复制。

猜你喜欢

转载自blog.csdn.net/Sweeneyzuo/article/details/84333023