一 HDFS 写文件流程

1 流程

HDFS的读写流程_hdfs

  1. 调用客户端的对象 DistributedFileSystem 的 create 方法;
  2. DistributedFileSystem 会发起对 namenode 的一个 RPC 连接,请求创建一个文 件,不包含关于 block 块的请求。namenode 会执行各种各样的检查,确保要创建的 文件不存在,并且客户端有创建文件的权限。如果检查通过,namenode 会创建一个文 件(在 edits 中,同时更新内存状态),否则创建失败,客户端抛异常 IOException。
  3. NN 在文件创建后,返回给 HDFSClient 可以开始上传文件块。
  4. DistributedFileSystem返回一个FSDataOutputStream对象给客户端用于写数 据。FSDataOutputStream 封装了一个 DFSOutputStream 对象负责客户端跟 datanode 以及 namenode 的通信。
  5. FSDataOutputStream 对象将数据切分为小的数据包(64kb,core-default.xml: file.client-write-packet-size 默认值 65536),并写入到一个内部队列(“数 据队列”)。DataStreamer 会读取其中内容,并请求 namenode 返回一个 datanode 列表来存储当前 block 副本。列表中的 datanode 会形成管线,DataStreamer 将数 据包发送给管线中的第一个 datanode,第一个 datanode 将接收到的数据发送给第 二个 datanode,第二个发送给第三个。。。
  6. DFSOoutputStream 维护着一个数据包的队列,这的数据包是需要写入到 datanode 中的,该队列称为确认队列。当一个数据包在管线中所有 datanode 中写入完成,就从 ack 队列中移除该数据包。如果在数据写入期间 datanode 发生故障,则执行以下操 作
    (a) 关闭管线,把确认队列中的所有包都添加回数据队列的最前端,以保证故障节点下 游的 datanode 不会漏掉任何一个数据包。
    b) 为存储在另一正常 datanode 的当前数据块指定一个新的标志,并将该标志传送 给 namenode,以便故障 datanode 在恢复后可以删除存储的部分数据块。
    c) 从管线中删除故障数据节点并且把余下的数据块写入管线中另外两个正常的 datanode。namenode 在检测到副本数量不足时,会在另一个节点上创建新的副 本。
    d) 后续的数据块继续正常接受处理。
    e) 在一个块被写入期间可能会有多个 datanode 同时发生故障,但非常少见。只要 设置了 dfs.replication.min 的副本数(默认为 1),写操作就会成功,并且 这个块可以在集群中异步复制,直到达到其目标副本数(dfs.replication 默 认值为 3)。
  7. 如果有多个 block,则会反复从步骤 4 开始执行。
  8. 当客户端完成了数据的传输,调用数据流的 close 方法。该方法将数据队列中的剩余数据包写到 datanode 的管线并等待管线的确认
  9. 客户端收到管线中所有正常 datanode 的确认消息后,通知 namenode 文件写完了。
  10. namenode 已经知道文件由哪些块组成,所以它在返回成功前只需要等待数据块进行最 小量的复制。

二 HDFS 读文件流程

1 流程

HDFS的读写流程_hdfs_02

  1. 客 户 端 通 过 FileSystem 对 象 的 open 方 法 打 开 希 望 读 取 的 文 件 , DistributedFileSystem 对象通过 RPC 调用 namenode,以确保文件起始位置。对 于每个 block,namenode 返回存有该副本的 datanode 地址。这些 datanode 根据 它们与客户端的距离来排序。如果客户端本身就是一个 datanode,并保存有相应 block 一个副本,会从本地读取这个 block 数据。
  2. DistributedFileSystem 返回一个 FSDataInputStream 对象给客户端读取数据。该类封装了 DFSInputStream 对象,该对象管理着 datanode 和 namenode 的 I/O, 用于给客户端使用。客户端对这个输入调用 read 方法,存储着文件起始几个 block 的 datanode 地址的 DFSInputStream 连接距离最近的 datanode。通过对数据流反 复调用 read 方法,可以将数据从 datnaode 传输到客户端。到达 block 的末端时, DFSInputSream 关闭与该 datanode 的连接,然后寻找下一个 block 的最佳 datanode。客户端只需要读取连续的流,并且对于客户端都是透明的。
  3. 客户端从流中读取数据时,block 是按照打开 DFSInputStream 与 datanode 新建 连接的顺序读取的。它也会根据需要询问 namenode 来检索下一批数据块的 datanode 的位置。一旦客户端完成读取,就 close 掉 FSDataInputStream 的输入流。
  4. 在读取数据的时候如果 DFSInputStream 在与 datanode 通信时遇到错误,会尝试从 这个块的一个最近邻 datanode 读取数据。它也记住那个故障 datanode,保证以后 不会反复读取该节点上后续的 block。DFSInputStream 也会通过校验和确认从 datanode 发来的数据是否完整。如果发现有损坏的块,就在 DFSInputStream 试图 从其他 datanode 读取其副本之前通知 namenode。
  5. Client 下载完 block 后会验证 DN 中的 MD5,保证块数据的完整性。

注意:
namenode 告知客户端每个 block 中最佳的 datanode,并让客户端直接连到 datanode 检索数据。由于数据流分散在集群中的所有 datanode,这样可以使 HDFS 可扩展到大量的 并发客户端。同时,namenode 只需要响应 block 位置的请求,无需响应数据请求,否则 namenode 会成为瓶颈。

3 最近邻

hadoop 把网络看作是一棵树,两个节点间的距离是它们到最近共同祖先的距离和。通常可 以设置等级:

  1. 同一个节点上的进程
  2. 同一机架上的不同节点
  3. 同一数据中心中不同机架上的节点
  4. 不同数据中心中的节点