BlockPoolSlice: 管理一个Block Pool在指定存储目录下的所有block.由于Data Node可以定义多个存储目录,所以Block Pool的block可能会分布在多个存储目录下,一个Block Pool会拥有多个BlockPool
Slice对象,这个BlockPool对应的所有BlockPoolSlice对象共同管理Block Pool中所有的Block
FSVolumeImpl: 管理Data Node一个存储目录下所有的数据块。由于一个存储目录可以存储多个Block Pool的数据块,所以FsVolumeImpl会持有这个存储目录中保存的所有BlockPoolSlice对象
FsVolumelList: Data Node可以定义多个存储目录,每一个存储目录下的数据块是使用FsVolumeImpl来管理的,所以Data Node定义了一个FsVolumelList保存Data Node上所有的FsVolumeImpl对象,FsVolumelList对FsDatasetImpl提供类似磁盘的服务
一 Data Node数据块副本的状态
FsDatasetImpl会有一个ReplicaMap对象,维护着Data Node上所有所有数据块副本的状态。
FINALIZED: Data Node上已经完成写操作的副本
RBW: Data Node上由客户端创建的正在进行写的副本,还没有完成写操作
RUR(Replica Under Recovery): 正在进行block恢复时的副本
RWR(Replica Waiting To Be Recovered): 如果Data Node宕机或者重启 ,所有RBW状态的的副本在Data Node重启之后,都将被加载为
RWR状态,RWR会等待块恢复操作
TEMPORARY: Data Node之间复制数据块,或者进行集群数据块平衡操作时,正在写入的副本状态就是临时状态,这个和RBW相比较,对于客户端是不可见的
二 BlockPoolSlice分析
Block Pool在每一个存储目录下都会有一个Block Pool目录存储数据块,那么BlockPoolSlice就是管理这个目录下所有的数据块。
如图示:就是管理着这个BP-54063261-192.168.3.101-1484123867096
目录下所有的数据块
一个块池目录可能存在current/finalized/rbw/tmp/layzPersist等文件目录。
Current目录包含了finalized、rbw以及lazyPersist等目录。
Finalized:保存了所有的FINALIZED状态的副本
Rbw:保存了RBW,RUR,RWR等状态的副本,tmp目录保存了TEMPORARY状态的副本。
以blk开始,不以meta结尾的就是数据块文件
以blk开始,以meta结尾则是数据块文件的校验文件
主要的字段
//blockpool id
privatefinal String bpid;
//这个Block Pool属于哪一个命名空间卷
privatefinal FsVolumeImpl volume;
//blockpool目录下的current目录
privatefinal File currentDir;
//finalized存储的目录
privatefinal File finalizedDir;
//lazypersist存储目录
privatefinal File lazypersistDir;
//RBW存储目录
privatefinal File rbwDir;
//存储TEMPORARY状态副本的目录
privatefinal File tmpDir;
BlockPoolSlice在构造的时候,会初始化finalized,tmp,rbw等字段
另外BlockPoolSlice提供了一些操作数据块副本的方法
在Block Pool目录中创建数据块副本包括以下几种情况:
>>创建TEMPORARY,RBW以及FINALIZED状态的副本。
File createTmpFile(Blockb) throws IOException {
Filef = newFile(tmpDir,b.getBlockName());
return DatanodeUtil.createTmpFile(b,f);
}
File createRbwFile(Blockb) throws IOException {
Filef = newFile(rbwDir,b.getBlockName());
return DatanodeUtil.createTmpFile(b,f);
}
>>获取Block Pool 目录下副本状态
/**
*获取当前block pool目录下所有数据块副本的状态,并且使用ReplicaMap来保存
*这些副本块的状态
*@param volumeMap
*@param lazyWriteReplicaMap
*@throws IOException
*/
voidgetVolumeMap(ReplicaMap volumeMap, final RamDiskReplicaTracker lazyWriteReplicaMap)
throws IOException {
//恢复lazypersist状态的副本
if (lazypersistDir.exists()) {
int numRecovered =moveLazyPersistReplicasToFinalized(lazypersistDir);
}
//将所有FINALIZED状态的副本加入ReplicaMap
addToReplicasMap(volumeMap, finalizedDir, lazyWriteReplicaMap, true);
//将所有RBW状态的副本加入ReplicaMap中
addToReplicasMap(volumeMap, rbwDir, lazyWriteReplicaMap, false);
}
三 FsVolumempl分析
DataNode 可以配置多个存储目录存储数据块文件,每一个存储目录下的数据块文件都由FsVolumeImpl管理,而每一个存储目录又可以可以保存多个Block Pool的数据块,其中每一个BlockPool 数据块的管理都是由BlockPoolSlice来管理
我们可以这样理解:
FsVolumeImpl管理的是一个存储目录下所有的数据块文件,不区分这些块属于哪一个BlockPool
BlockPoolSlice只是针对某一个BlockPool下的数据块文件进行管理
FsVolumeSpi接口:
/*获取当前存储目录的StorageUuID*/
public StringgetStorageID();
/*获取当前存储目录下BlockPool列表*/
public String[]getBlockPoolList();
/*获取当前存储目录下可以使用的存储空间*/
publiclong getAvailable()throws IOException;
/*获取当前存储目录的基准路径*/
public StringgetBasePath();
/*获取当前存储目录的路径*/
public StringgetPath(String bpid)throws IOException;
/*获取指定块池在当前存储目录下的finalized目录*/
public FilegetFinalizedDir(String bpid) throws IOException;
/*获取当前存储目录的存储类型:内存,磁盘,固态硬盘还是archive*/
public StorageTypegetStorageType();
/*对RBW状态的数据块预留磁盘空间,这样写数据时就不会出现磁盘空间不足*/
publicvoid reserveSpaceForRbw(longbytesToReserve);
/*释放预留的空间*/
publicvoid releaseReservedSpace(longbytesToRelease);
/*判断当前卷是否有与之对应的持久化磁盘*/
publicboolean isTransientStorage();
FsVolumeImpl比较重要的字段和方法
privatefinal FsDatasetImpl dataset;
//当前存储目录对应的StorageDirectory ID
privatefinal String storageID;
//存储类型
privatefinal StorageType storageType;
//<block pool id,BlockPoolSlice>映射
privatefinal Map<String, BlockPoolSlice>bpSlices
=newConcurrentHashMap<String, BlockPoolSlice>();
//当前存储目录下的current文件
privatefinal File currentDir;
//当前磁盘使用状况工具类
privatefinal DF usage;
//当前存储目录预留的磁盘空间大小
privatefinal longreserved;
private CloseableReferenceCountreference = newCloseableReferenceCount();
//为rbw预留的磁盘空间
privateAtomicLongreservedForRbw;
FsVolumeImpl方法主要分为四个部分:
>>获取存储目录的存储情况
getDfsUsed/getBlockPoolUsed/getCapacity/getAvailable/getReservered/等。底层都是调用BlockPoolSlice对应方法
比如:
publiclong getDfsUsed()throws IOException {
long dfsUsed =0;
synchronized(dataset) {
for(BlockPoolSlice s :bpSlices.values()) {
dfsUsed += s.getDfsUsed();
}
}
return dfsUsed;
}
>>获取存储目录的子目录
包括getCurrentDir、getRbwDir等
>>数据块操作
包括添加数据块副本,获取数据块副本数等操作
这些方法底层都是调用BloclkPoolSlice的对应方法
File addFinalizedBlock(String bpid, Block b,File f, longbytesReservedForRbw) throwsIOException {
releaseReservedSpace(bytesReservedForRbw);
return getBlockPoolSlice(bpid).addBlock(b,f);
}
>>BlockPool操作
比如在当前存储目录下添加或者删除一个BlockPool的存储
void addBlockPool(String bpid, Configurationconf) throws IOException {
Filebpdir = newFile(currentDir,bpid);
BlockPoolSlicebp = newBlockPoolSlice(bpid,this, bpdir, conf);
bpSlices.put(bpid,bp);
}
四 FsVolumeList分析
HDFS提供了一个接口类FsVolumeList,使用一个集合持有所有的FsVolumeImpl对象,然后进行所有存储目录数据块的管理
比较重要的字段
/*
*选择一个存储目录对应的FsVolumeImpl对象来存放数据块副本
*目前有2种策略:
* AvailableSpaceVolumeChoosingPolicy
*选择有更多可用空间的的存储目录来存放副本
* RoundRobinVolumeChoosingPolicy
*轮询直到选择出第一个有足够空间存储目录来存放副本
*/
privatefinal VolumeChoosingPolicy<FsVolumeImpl>blockChooser;
privatefinal AtomicReference<FsVolumeImpl[]>volumes =
newAtomicReference<>(new FsVolumeImpl[0]);
它主要提供一下三种操作:
>>获取DataNode节点状态操作:FsVolumeList提供了获取DataNode总容量,剩余总量,dfs使用量和数据块信息等方法
>>BlockPool相关操作:提供了添加和删除BlockPool的操作