功能说明
- 管理存储目录下的数据块和方法
持有FSVolumnList对象 - 对Datanode存储空间进行操作
持有DataStorage对象 - 维护Datanode上所有的块副本信息
持有ReplicaMap对象
目录,块,副本?
ReplicaMap
结构为Map<String,Map<Long,ReplicaInfo>> map,块池(BlockPoolId)和数据块副本的映射关系。
这个信息是块池汇报的吗?
ReplicaInfo
描述DN上保存的数据块副本信息,对外提供了
- getBlockFile()
获取副本的数据块文件 - getMetaFile()
获取副本的校验文件 - getVolume()
获取副本存储目录的FSVolumeImpl对象 - getStorageUuid()
获取副本存储目录的StorageUuid信息 - unlinkBlock()
去除副本的链接信息,通过复制一份出来。
不同状态的副本,加载到ReplicaMap时ReplcaInfo对应不同的子类
- ReplicaBeingWritten
副本正在通过管道Pipeline写入
ReplicaUnderRecovery
副本正在恢复操作
ReplicaWaittingToBeRecovered
副本正在等待恢复
FinalizedReplica
副本已经写完成,并且提交
FsDatasetSpi
FsDatasetImpl的根接口包含Datanode上管理和操作数据块副本的接口。
- 底层FSVolumnList以及DataStorage的方法
- 操作数据块的方法
getStoredBlock,getBlockInputStream,append,finalizeBlock等 - 缓存相关操作
cache,uncache - 操作块池的方法
addBlockPool,shutdownBlockPool,deleteBlockPool - 数据块汇报的方法
getStorageReports,getBlockReports,getCacheReport
块,缓存,存储
FsDataSetImpl
字段
- datanode
- datanodeStorage
- storageMap
目录对应DataStorage对象 - volumes
- volumeMap
副本块相关的 - cacheManager
缓存相关的,数据块缓存到内存工具类 - asyncDiskService
方法
- 操作块FsDatasetImpl
- 操作存储DataStorage
- 操作数据块相关方法
- 操作缓存的方法
- 操作块池的方法
- 数据块汇报的方法
- 回收站相关的方法
获取块FsDataSetImpl方法
- getVolumes
- getVolume
获取DataStorage方法
- getStorage
- getVolumeInfoMap
获取FsVolumeImpl对应的存储目录信息
这个信息是怎么放进去的?
获取数据块副本信息方法
获取复制块
FsDatasetImpl中volumeMap(ReplicaMap类型)记录了DN所有的块信息,(块池和块的映射关系)
- 获取ReplicaInfo对象
- 获取副本数据块文件的引用
info.getBlockFile(); - 构造Block对象并返回
new Block(blkId,blockfile.length(),gs);
副本校验
- DirectoryScanner.run()获取内存和磁盘不一致的引用
- checkAndUpdate 同步状态
同步、更新引用、标记损坏(长度不一致),修改时间戳.
更新引用需要 BlockPoolSlice.resolveDuplicateReplicas来判断保存哪一个。
损坏块需要报告给NN.
操作副本块方法
以下的方法全是Synchronized的
createTemporary
- 平衡时,或者数据块复制时创建的。
- BlockReceiver构造时创建
- 其他读线程不可见
是不是tem目录下的内容,没有被加载到BlockPoolSlice
- Datanode向该副本写数据
createTemporary实现
- 检查内存中是否已经有该副本
- 获取一个FsVolumeImpl存储该副本
可以是根据空闲/轮询 - 在该存储目录下创建tmp目录
- 构造ReplicaInfo信息
ReplicaInPipeline newReplicaInfo = new ReplicaInPipeline(b.getBlockId,b.getGS(),v,f.getParentFile());
createRbw
和createTemporary类似,只不过目录在rbw目录下
其他读线程可见
recoverRbw
恢复Rbw状态的副本
- 停止之前的写入线程
- 检查时间戳,更新时间戳
- 丢弃没有ack的数据
ack返回的是一个值,表示之前的确认了。
启动时调用吧?
convertTemporaryToRbw
副本数据块接收完成之后,需要move到Rbw目录下,才可以对读线程可见。
- 检查时间戳,目录,长度等
- 移动目录
- 构造新的ReplicaInfo(ReplicaBeingWritten),在volumeMap中更新。
append
对FINALIZED状态的副本执行追加写操作。
- 从缓存中移除该块
保持一致性,缓存部分解释 - 去掉硬链接
升级存储快照时,为了节省空间,previous中和current之间存的是硬链接。 - 文件和meta复制到RBW目录
- 构造新的ReplicaInfo,在volumeMap中更新
recoverAppend
恢复失败的追加写,在BlockReceiver的构造方法中调用。调用完recoverAppend之后,数据流管道中所有副本的状态都恢复到RBW状态。
NN控制恢复吗?
- 调用recoverCheck()
如果副本是FINALIZED状态,那么调用append方法。
如果副本是RBW状态,那么更新副本时间戳即可。
副本如果在TEM目录下,其他线程不可见 - 检查时间戳,长度
- 如果副本处于写状态,更新写线程
replica会保存副本的写线程,writer属性 - 确认长度一致
recoverClose
Datanode写操作恢复时,DataXceiver的writeBlock中调用的。
除了调用者不同,实现基本相同。
BlockReceiver和DataXceiver做啥事情的?
finalizeBlock
用户提交数据块,Datanode完成数据块写操作并成功接收数据流管道下游的ack时,PacketResponder.run()方法调用finalizeBock()提交数据块。
- 检查副本状态
RUR直接转状态即可 - 移动目录
其他的状态需要FsVolumeImpl.addFinalizedBlock()移动目录 - 更新replicaMap
构造新的FinalizedReplica对象,更新FsDatasetImpl.volumeMap
unfinalizeBlock
删除TEMPORARY状态的副本
- 检查副本状态
- 删除数据块文件和校验和文件
- volumeMap中移除副本的ReplicaInfo
invalidate
删除RBW状态和FINALIZED状态的副本
- 判断是否短路读,短路读从内存中删除
- 异步从磁盘删除文件
FsDatasetAsyncDiskService
initReplicaRecovery
写文件时异常退出,没有正常关闭文件时,需要对最后一个快进行恢复操作,然后进行后续操作。
- NN节点选择主恢复节点
Namenode初始化块,选择主恢复节点控制数据块恢复流程。
NN心跳携带LeaseRecovery指令 - 主恢复节点选择状态
主节点调用InterDatanodeProtocol接口和其他的Datanode通信
initReplicaRcovery获取数据流中各个Datanode副本恢复信息,选取最好的副本状态
updateReplicaUnderRecovery方法同步所有Datanode上副本恢复。
执行步骤:
1) 获取当前的节点副本信息
2) 如果副本有写线程,通知该线程
replicaInpipeline.stopwriter(xceiverStopTimeout);
3)判断恢复操作是否有效
4)构造RepliaUnderRecovery对象,放入volumeMap中。
目前缺的是调用顺序
updateReplicaUnderRecovery
主恢复节点调用InterDatanodeProtocol.initReplicaRecovery方法后,回复数数据流管道所有块的副本状态。找到最好的状态作为目标状态。优先选择FINALIZED,否则选择长度最短的。
主恢复节点对列表中所有datanode调用InterDatanodeProtocol.updateReplicaUnderRecovery同步副本状态。
1)检查副本是不是RUR状态
2)检查副本数据块文件和校验文件是否匹配
3)如果长度比目标长,那么截断数据文件,重新计算校验和
4)更新副本状态为Finalized状态,更新volumeMap