Datanode 可以配置多个存储目录存储数据块文件。每一个存储目录下的数据块管理功能都由一个单独的 FsVolumelmpl 对象负责。而每一个存储目录又可以保存多个块池的数据块,其中每一个块池数据块的管理都由一个 BlockPoolSlice 对象负责,因此 FsVolumelmpl 对象会持有多个 BlockPoolSlice 对象的引用。

1. FsVolumeSpi 接口

FsVolumeSpi 是 FsVolumelmpl 的根接口

截屏2021-05-21 下午4.23.53

2. FsVolumeImpl

FsVolumeImpl 实现了 FsVolumeSpi 接口

2.1. FsVolumeImpl 字段

截屏2021-05-21 下午4.49.07

2.2. FsVolumeImpl 方法

截屏2021-05-21 下午5.04.19

3. FsVolumeList 实现

Datanode 可以设置多个存储目录,每一个存储目录的数据块都是由一个 FsVolumelmpl 对象负责管理的。对于Datanode定义的多个存储目录,HDFS提供了接口类 FsVolumeList 进行统一管理,FsVolumeList 使用一个线性表保存所有的FsVolumeImpl 对象,并提供统一的接口在所有的 FsVolumeImpl 对象上执行操作。

3.1. 字段

FsVolumeList 定义了两个重要的字段。

  1. List<FsVolumeImpl> volumes: 保存 Datanode 配置的所有 FsVolumeImpl 对象。集合是一个只读集合,不可以进行修改。
  2. VolumeChoosingPolicy<FsVolumeImpl>blockChooser: 选择一个存储目录对应的 FsVolumeImpl 对象来存放数据块副本。目前有两种策略。
    • AvailableSpaceVolumeChoosingPolicy: 选择有更多可用空间的存储目录来存放副本
    • RoundRobinVolumeChoosingPolicy: 轮询策略,轮询直到选择出第一个有足够空间的存储目录来存放副本。

3.2. 方法

FsVolumeList 主要提供以下三种类型的操作。

  1. 获取 Datanode 节点状态操作

    FsVolumeList 提供了获取 Datanode 总容量、剩余容量、dfs 使用量和数据块信息 (getVolumeMap() 和 getAllVolumesMap())等方法。

  2. BlockPool相关:

    FsVolumeList 提供了添加和删除 BlockPool的方法(addBlockPool))、removeBlockPool()), 用于在所有的 FsVolumeImpl 上添加和删除一个块池存储目录。

  3. 获取一个可以存储副本的存储目录

    对应于 getNextVolume()方法,在 blockChooser 字段上调用 chooseVolume() 方法。可以有两种策略,
    即轮询与可用空间优先。Datanode 在存储一个新的数据块副本时,会首先调用这个方法获取一个可以存储这个数据块副本的存储目录,然后在这个存储目录对应的 FsVolumeImpl 对象上调用 createRbwFile() 或者 createTemporaryFile() 创建临时数据块文件进行写操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /** 
    * Get next volume.
    *
    * @param blockSize free space needed on the volume
    * @param storageType the desired {@link StorageType}
    * @return next volume to store the block in.
    */
    FsVolumeReference getNextVolume(StorageType storageType, long blockSize)
    throws IOException {
    // Get a snapshot of currently available volumes.
    final FsVolumeImpl[] curVolumes = volumes.get();
    final List<FsVolumeImpl> list = new ArrayList<>(curVolumes.length);
    for(FsVolumeImpl v : curVolumes) {
    if (v.getStorageType() == storageType) {
    list.add(v);
    }
    }
    return chooseVolume(list, blockSize);
    }