一、概述

在 HDFS 客户端实现中,最重要也最复杂的一部分就是文件的读写操作😯~

二、理论笔记

三、实现

3.1. 打开文件

当用户读取一个 HDFS 文件时,首先会调用 DistributedFileSystem.open() 方法打开这个文件,并获取文件对应的 FSDataInputStream 输入流,然后在这个 FSDataInputStream 对象上调用 read() 方法读取数据。

3.2. 读文件

HDFS 目前实现的读操作有三个层次,分别是网络读、短路读(short circuit read)以及零拷贝读(zero copy read),它们的读取效率依次递增。

  1. 网络读: 网络读是最基本的一种HDFS读,DFSClient 和 Datanode 通过建立 Socket连接传输数据。
  2. 短路读: 当 DFSClient 和保存目标数据块的 Datanode 在同一个物理节点上时,DFSClient 可以直接打开数据块副本文件读取数据,而不需要 Datanode 进程的转发。
  3. 零拷贝读: 当 DFSClient 和缓存目标数据块的 Datanode 在同一个物理节点上时,DFSClient 可以通过零拷贝的方式读取该数据块,大大提高了效率。而且即使在读取过程中该数据块被Datanode 从缓存中移出了,读取操作也可以退化成本地短路读

HdfsDataInputStream.read()方法就实现了层次读取。HdfsDataInputStream.read() 方法首先会调用HasEnhancedByteBufferAccess.read() 方法方法尝试进行零拷贝读取,如果当前配置不支持零拷贝读取模式,则抛出异常,然后调用 ByteBufferUtil.fallbackRead() 静态方法退化成短路读或者网络读。HasEnhancedByteBufferAccess.read() 方法定义了零拷贝读取的实现,而 InputStream.read() 方法则定义了短路读和网络读的实现。

3.2.1. 零拷贝读

DFSInputStream 实现了 HasEnhancedByteBufferAccess 接口的 read()方法,提供了以零拷贝模式读取数据块的功能。

3.2.2. 普通读

当 read() 方法执行零拷贝读操作失败后,会调用 ByteBufferUtil.fallbackRead() 退化为一个普通的读操作。

1
2
3
4
buffer = ByteBufferUtil.fallbackRead(this, bufferPool, maxLength);
if (buffer != null) {
getExtendedReadBuffers().put(buffer, bufferPool);
}

ByteBufferUtil.fallbackRead() 判断传入参数的 InputStream (DFSInputStream)是否支持 ByteBuffer Read(实现了ByteBufferReadable 接口)。如果支持则直接将数据读取至 ByteBuffer 中,否则读取到 ByteBuffer:array() 字节数组中。

  1. 短路读

    BlockReaderLocal 类实现了本地短路读取功能,也就是当客户端与 Datanode 在同一台机器上时,客户端可以绕过Datanode 进程直接从本地磁盘读取数据。

  2. 网络读

    网络读是最基本的一种 HDFS 读,DFSClient 和 Datanode 通过建立 Socket 连接传输数据。