BlocksMap 是 Namenode 上与数据块相关的最重要的类,它管理着 Namenode 上数据块的元数据,包括当前数据块属于哪个 HDFS 文件,以及当前数据块保存在哪些 Datanode 上。当 Datanode 启动时,会对 Datanode 的本地磁盘进行扫描,并将当前 Datanode 上保存的数据块信息汇报到 Namenode。Namenode 收到 Datanode 的汇报信息后,会建立数据块与保存这个数据块的数据节点的对应关系,并将这个信息保存在 BlocksMap 中。所以无论是获取某个数据块对应的 HDFS 文件,还是获取数据块保存在哪些数据节点上,都需要通过 BlocksMap 对象

静态变量

  1. /** Constant {@link LightWeightGSet} capacity. */
  2. private final int capacity;
  3. private GSet<Block, BlockInfo> blocks;
  4. private final LongAdder totalReplicatedBlocks = new LongAdder();
  5. private final LongAdder totalECBlockGroups = new LongAdder();

这里面一共有两个静态变量比较重要,一个是 capacity,另外一个是 blocks

blocks 是存放数据的具体的实现类,但是,我们所看的 GSet 只是一个接口

GSet和LightWeightGSet

blocks 字段对应的是 GSet 类型的数据,用于存放真实的数据

首先说一下 GSet 和 LightWeightGSet 的关系

GSet 是接口,LightWeightGSet 是 GSet 的实现类

  • LightWeightGSet 是一个低内存占用的 GSet 实现类,它使用数组来存储元素,使用链表解决碰撞

  • LightWeightGSet 不会调整内部长度的大小,所以它的大小在创建的时候就被指定了

  • 因为不会扩容所以也不会存在扩容引起的问题,比如重新进行hash值

  • 这个类不支持为 null 的元素

  • 这个类不是线程安全的

  • LightWeightGSet K为查找元素的类型, E必须是K的子类

LightWeightGSet实现

LightWeightGSet 类实现于 GSet 接口,采用数据+链表的方式进行存储
image.png

  1. /**
  2. * An internal array of entries, which are the rows of the hash table.
  3. * The size must be a power of two.
  4. */
  5. protected LinkedElement[] entries;

核心就是 LinkedElement[] entries 数组

  1. /**
  2. * @param recommended_length Recommended size of the internal array.
  3. */
  4. public LightWeightGSet(final int recommended_length) {
  5. final int actual = actualArrayLength(recommended_length);
  6. if (LOG.isDebugEnabled()) {
  7. LOG.debug("recommended=" + recommended_length + ", actual=" + actual);
  8. }
  9. entries = new LinkedElement[actual];
  10. hash_mask = entries.length - 1;
  11. }

这里其实就是对 LightWeightGSet 进行初始化操作,首先确定数组的大小,数据的大小根据入参recommended_length 进行确定

然后会用 actualArrayLength 进行处理,验证是否在 1 到 2^30 之间

并处理成一个大于等于且是2的倍数的 actual 值返回

然后根据 actual 值创建一个数组就行了

  1. @Override
  2. public E put(final E element) {
  3. // validate element
  4. if (element == null) {
  5. throw new NullPointerException("Null element is not supported.");
  6. }
  7. LinkedElement e = null;
  8. try {
  9. e = (LinkedElement)element;
  10. } catch (ClassCastException ex) {
  11. throw new HadoopIllegalArgumentException(
  12. "!(element instanceof LinkedElement), element.getClass()="
  13. + element.getClass());
  14. }
  15. // find index
  16. // 就是通过hash值与数组长度取余,计算出下标就行了.
  17. final int index = getIndex(element);
  18. // remove if it already exists
  19. // 移除已经存在的key
  20. final E existing = remove(index, element);
  21. // insert the element to the head of the linked list
  22. // 将element加入到链表的头里面, 并将数组的槽位指定到数组中.
  23. modification++;
  24. size++;
  25. e.setNext(entries[index]);
  26. entries[index] = e;
  27. return existing;
  28. }

获取元素,就是通过 hash 值获取到数组元素的下标,然后遍历下标上对应的链表

  1. @Override
  2. public E get(final K key) {
  3. //validate key
  4. if (key == null) {
  5. throw new NullPointerException("key == null");
  6. }
  7. //find element
  8. final int index = getIndex(key);
  9. for(LinkedElement e = entries[index]; e != null; e = e.getNext()) {
  10. if (e.equals(key)) {
  11. return convert(e);
  12. }
  13. }
  14. //element not found
  15. return null;
  16. }

使用限制

容量在使用的时候必须指定大小

value 必须是 key 的子类,并且要实现LinkedElement接口