Hadoop[1] - HDFS的读写流程

230 阅读4分钟

首先要了解Hadoop Java API中的类,读写文件涉及到这些类:

  • FileSystem
  • DistributedFileSystem
  • FSDataInputStream
  • DFSInputStream
  • FSDataOutputStream
  • DFSOutputStream
  • DataStreamer

读取文件

访问文件流程:

  1. 客户端调用FileSystem类的实例方法open对文件进行访问

  2. open方法具体由DistributedFileSystem中的实例方法open实现

  3. DistributedFileSystem通过 RPC 调用 namenode,确定文件起始块的位置,对于每一个块,返回存有该块副本的 datanode 地址

  4. 根据客户端与这些 datanode 的网络拓扑距离将这些 datanode 进行排序,返回一个FSDataInputStream对象给客户端

    • FSDataInputStream类转而封装DFSInputStream对象,该对象管理着 datanode 和 namenode 的 I/O

基于以上,读文件流程:

  1. 客户端对FSDataInputStream调用read方法

  2. DFSInputStream对象连接距离最近的[文件中第一个块所在的 datanode]

  3. 对数据流反复调用read方法,可以将数据从 datanode 传输到客户端

    • DFSInputStream会通过校验和确认数据的完整性
    • 若在读取数据过程中通信遇到错误数据损坏DFSInputStream会尝试从这个块的另一个最近的 datanode 读取数据副本
    • 同时记录故障的 datanode,保证以后不会反复读取故障节点后续的块
  4. 到达块的末端时,DFSInputStream关闭与该 datanode 的连接,然后寻找下一个块的最佳 datanode

  5. 读取完毕后,调用FSDataInputStreamclose方法

写入文件

首先是创建文件的流程:

  1. 客户端调用FileSystem类的实例方法create尝试创建文件

  2. create方法具体由DistributedFileSystem中的实例方法create实现

  3. DistributedFileSystem通过 RPC 调用 namenode,在文件系统的命名空间新建一个文件

  4. namenode 执行检查确保该文件不存在,以及客户端有创建该文件的权限

    • 若检查通过,namenode 会为创建新文件记录一条记录
    • 否则创建失败,并向客户端抛出IOException
  5. 创建成功后,DistributedFileSystem向客户端返回一个FSDataOutputStream对象

由此客户端可以开始写入数据

写文件流程:

  1. 在客户端写入数据时,DFSOutputStream将它分成一个个的数据包,并写入内部队列,称为数据队列

  2. DataStreamer处理数据队列,其责任是:

    • 挑选出适合存储数据副本的一组 datanode

    • 以上为依据,要求 namenode 分配新的数据块

    • 这一组 datanode 构成一个管线,假设副本数为3,则管线中有3个节点

      1. DataStreamer将数据包流式传输到管线中第一个 datanode(称为dn1)
      2. dn1 存储数据包并将数据包发给 dn2,依此传递
  3. DFSOutputStream也维护着一个内部数据包队列来等待 datanode 的收到确认回执,称为确认队列(ack queue),收到管线中所有 datanode 的确认消息后,该数据包才会从确认队列中删除

  4. 若任何 datanode 在数据写入期间发生故障,则执行:

    1. 关闭管线,确认把队列中的所有数据包都添加回数据队列的最前端,以确保故障节点下游的 datanode 不会漏掉任何一个数据包
    2. 为存储在另一正常 datanode 的当前数据块制定一个新的标识,并将该标识传送给 namenode,以便故障 datanode 在恢复后可以删除存储的部分数据块
    3. 从管线中删除x个故障 datanode,基于 n-x(n为副本数)个 datanode 构建一条新管线,余下的数据块写入管线中正常的 datanode
    4. namenode 注意到块副本量不足时,会在另一个节点创建一个新的副本,后续的数据块继续正常接受处理
  5. 客户端完成了数据的写入后,调用close方法,该操作将剩余的所有数据包写入 datanode 管线,并在联系到 namenode 告知其文件写入完成之前,等待确认

  6. 由于 namenode 已经知道文件由哪些块组成(因为Datastreamer请求分配数据块),所以它在返回成功前,只需要等待数据块进行最小量的复制