BlocksMap 是 Namenode 上与数据块相关的最重要的类,它管理着 Namenode 上数据块的元数据,包括当前数据块属于哪个 HDFS 文件,以及当前数据块保存在哪些 Datanode 上。当 Datanode 启动时,会对 Datanode 的本地磁盘进行扫描,并将当前 Datanode 上保存的数据块信息汇报到 Namenode。Namenode 收到 Datanode 的汇报信息后,会建立数据块与保存这个数据块的数据节点的对应关系,并将这个信息保存在 BlocksMap 中。所以无论是获取某个数据块对应的 HDFS 文件,还是获取数据块保存在哪些数据节点上,都需要通过 BlocksMap 对象
静态变量
/** Constant {@link LightWeightGSet} capacity. */
private final int capacity;
private GSet<Block, BlockInfo> blocks;
private final LongAdder totalReplicatedBlocks = new LongAdder();
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 接口,采用数据+链表的方式进行存储
/**
* An internal array of entries, which are the rows of the hash table.
* The size must be a power of two.
*/
protected LinkedElement[] entries;
核心就是 LinkedElement[] entries 数组
/**
* @param recommended_length Recommended size of the internal array.
*/
public LightWeightGSet(final int recommended_length) {
final int actual = actualArrayLength(recommended_length);
if (LOG.isDebugEnabled()) {
LOG.debug("recommended=" + recommended_length + ", actual=" + actual);
}
entries = new LinkedElement[actual];
hash_mask = entries.length - 1;
}
这里其实就是对 LightWeightGSet 进行初始化操作,首先确定数组的大小,数据的大小根据入参recommended_length 进行确定
然后会用 actualArrayLength 进行处理,验证是否在 1 到 2^30 之间
并处理成一个大于等于且是2的倍数的 actual 值返回
然后根据 actual 值创建一个数组就行了
@Override
public E put(final E element) {
// validate element
if (element == null) {
throw new NullPointerException("Null element is not supported.");
}
LinkedElement e = null;
try {
e = (LinkedElement)element;
} catch (ClassCastException ex) {
throw new HadoopIllegalArgumentException(
"!(element instanceof LinkedElement), element.getClass()="
+ element.getClass());
}
// find index
// 就是通过hash值与数组长度取余,计算出下标就行了.
final int index = getIndex(element);
// remove if it already exists
// 移除已经存在的key
final E existing = remove(index, element);
// insert the element to the head of the linked list
// 将element加入到链表的头里面, 并将数组的槽位指定到数组中.
modification++;
size++;
e.setNext(entries[index]);
entries[index] = e;
return existing;
}
获取元素,就是通过 hash 值获取到数组元素的下标,然后遍历下标上对应的链表
@Override
public E get(final K key) {
//validate key
if (key == null) {
throw new NullPointerException("key == null");
}
//find element
final int index = getIndex(key);
for(LinkedElement e = entries[index]; e != null; e = e.getNext()) {
if (e.equals(key)) {
return convert(e);
}
}
//element not found
return null;
}
使用限制
容量在使用的时候必须指定大小
value 必须是 key 的子类,并且要实现LinkedElement接口