理解FSImage,首先需要理解INode。
下面是Hadoop2.6.0下的INode类图。
INode的源码如下
public abstract class
INode implements
INodeAttributes,
Diff.Element<byte[]>,
AuthorizationProvider.INodeAuthorizationInfo {
private INode
parent = null;
final boolean
isRoot() {
return getLocalNameBytes().length==0;
}
/**
* Information used for updating the blocksMap when deleting files.
*/
public static class
BlocksMapUpdateInfo {
/**
* The list of blocks that need to be removed from blocksMap
*/
private final List<Block>
toDeleteList;
...
}
/**
* INode feature such as {@linkFileUnderConstructionFeature}
* and {@linkDirectoryWithQuotaFeature}.
*/
public interface
Feature {
}
}
属性就一个parent,它的作用是在快照当中进行版本判断。其余都是与INode权限相关方法。最终要的是有一个内部类BlocksMapUpdateInfo用来在删除文件的时候更新blockMap。还有一个Feature接口,这个Feature有多个具体实现如下:
可以看出来Feature具体类是用来表现INode的特性的,AclFeature是用来表示ACL的DirectoryWithQuotaFeature是用来记录配额信息的,FileUnderConstructionFeature使用来记录在读取文件的信息的,XAttrFeature是用来保存额外信息的,下面三个都是和快照实现相关的特性类。
我们可以看到Feature是作为INodeWithAdditionalFields的具体属性的,这个类做为一个抽象类,在INode基础上增加了作为文件、目录或者软连接所必要的几个信息,具体为
final private long
id;
private byte[]name=null;
private long
permission =0L;
private long
modificationTime =0L;
private long
accessTime =0L;
protected
Feature[] features=EMPTY_FEATURE;
其中大部分属性看名字就知道作用,唯独permission具体说明下:
首先看下INodeWithAdditionalFields的源码
static enum
PermissionStatusFormat {
MODE(null,16),
GROUP(MODE.BITS,25),
USER(GROUP.BITS,23);
final
LongBitFormat BITS;
static
String getUser(longpermission)
{
final int n = (int)USER.BITS.retrieve(permission);
return
SerialNumberManager.INSTANCE.getUser(n);
}
...
}
已及相关类LongBitFormat的源码:
public class
LongBitFormat implementsSerializable {
private static final long
serialVersionUID
= 1L;
private finalStringNAME;
/** Bit offset */
private final int
OFFSET;
/** Bit length */
private final int
LENGTH;
/** Minimum value */
private final long
MIN;
/** Maximum value */
private final long
MAX;
/** Bit mask */
private final long
MASK;
}
结合来看很容易知道permission用一个long型保存了MODE,GROUP,USER三个具体内容。
此外INodeWithAdditionalFields抽象类,在INode基础上对很多抽象方法进行了实现,主要还是权限相关和特性Feature及作为文件、目录软连接用的基础方法。
INodeFile类在INodeWithAdditionalFields基础上多了header属性和blocks属性,其中header同父类INodeWithAdditionalFields的permission一样也是用一个long类型记录多个属性值,具体为与文件相关的
PREFERRED_BLOCK_SIZE(null,48,1),
REPLICATION(PREFERRED_BLOCK_SIZE.BITS,12,1),
STORAGE_POLICY_ID(REPLICATION.BITS,BlockStoragePolicySuite.ID_BIT_LENGTH,
0);
相信看过Hadoop的一眼就能知道这些代表什么。blocks记录的是INodeFile和文件块之间的关系(Hadoop中的第一类关系),同时INodeFile类提供了block处理相关的几个方法,因为与fsImage相关性不大,这里就不分析了。
INodeDirectory类相较于INodeWithAdditionalFields只多了一个属性children,用来记录层级关系,这样就构成了树状结构。文件夹相关的配额信息等都通过提供的方法写入了INodeWithAdditionalFields的Feature上了。
INodeSymlink也只是比INodeWithAdditionalFields多了一个属性symlink用来标记软连接的目标。
INodeReference是一个抽象类,它拓展自INode类,所以INodeReference及其子类是可以添加到文件系统目录树中以替代原有的INodeFile节点的。INodeReference定义了referred字段,这个字段用于保存当前INodeReference类指向的INode节点,所以WithName和RstReference,referred字段就指向了WithCount对象,对于WithCount,referred指向了真正的INode对象。INodeReference还定义了getReferredINode()方法,在文件系统目录树的操作中,如果判断当前节点是一个引用节点,则会调用getReferredINode()方法获取INodeReference指向的INode对象。
具体源码如下
public abstract class INodeReference extends INode {
private INode referred;//指向的INode节点
public INodeReference(INode parent,INode referred){//这里用到了INode的字段parent
super(parent);
this.referred = referred;
}
//...
}
然后,我们在来看看WithCount类的实现。
WithCount类定义了一个集合字段withNameList用于保存所有指向这个WithCount对象的WithName对象集合。WithCount类还定义了addReference()方法,任何指向WithCount对象的WithName对象以及DstReference对象都需要调用这个方法来添加指向关系。对于指向这个WithCount对象的DstReference对象,addReference()方法会将这个对象设置为自己的父INode节点;而对于WithName对象,addReference()方法则将这个对象放入withNameList集合中保存。
public static class WithCount extends INodeReference {
//保存所有指向这个WithCount对象的WithName对象的集合
private final List<WithName> withNameList = new ArrayList<WithName>();
public WithCount(INodeReference parent,INode referred) {
super(parent,referred); //调用父类的构造方法,指向文件系统目录树中的INode
Preconditions.checkArgument(!referred.isReference());
refferred.setParentReferenct(this); //设置真实INode的父节点为当前WithCount对象
}
public void addReferenct(INodeReference ref){
if ( ref instanceof WithName) { //如果是WithName对象,则加入withNameList
WithName refWithName = (WithName) ref;
in i = Collections.binarySearch(withNameList, refWithName,WITHNAME_COMPARATOR);
Preconditions.checkState(i<0);
withNameList.add(-i-1,refWithName);
} else if (ref instanceo DstReference) { //如果是DstReference对象,则设置为父节点
setParentReference(ref);
}
}
//...
}
看完WithCount后,在看看WithName和DstReference。WithName类定义了name字段用于保存重命名前文件的名称,同事定义了lastSnapshotId字段用于保存WithName对象构造时源路径的快照版本号。DstReference类的实现就更简单了,只定义了一个dstSnapshotId字段用于保存重命名操作前目标路径的最新快照的版本号。WithName和DstReference在构造时都会调用父类的构造方法指向WithCount对象,同时还会调用WithCount.addReference()方法配置WithCount对象。
public static class WithName extend INodeReference {
private final byte[] name;//重命名前的文件名
private final int lastSnapshotId;
publicWithName(INodeDirectory parent,WithCount referred,bytep[] name,in lastSnapshotId){
super(parent,referred); //调用父类构造方法,指向WithCount节点
this.name = name;
this.lastSnapshotId = lastSnapshotId;
referred.addReferenct(this); //调用WithCount.addReferenct()
}
//...
}
public static class DstReference extends INodeReference {
private final int dstSnapshotId;
public DstReference (INodeDirectory parent,WithCount referred,fina int dstSnapshotId){
super(parent,referred);
this.lastSnapshotId = lastSnapshotId;
referred.addReferenct(this); //调用WithCount.addReferenct()
}
//..
}
看完了INode的类之间的关系及各自的作用那么理解FSImage的结构就容易了。首先上图
将内存中INode的相关信息持久化FSImage文件上。
FSImage最开始的8个字节为MAGIC_HEADER(value=“HDFSIMG1”)
中间为10个sections (通过FileSummary.parseDelimitedFrom(newByteArrayInputStream(summaryBytes));得到FileSummary)
分别为:{
0:name:”NS_INFO” 记录HDFS文件系统的全局信息,包括NameSystem的ID,当前已经分配出去的最大BlockID以及TransactionId等信息;具体字段
1:name: “INODE" 整个目录树所有节点数据,包括INodeFile/INodeDirectory/INodeSymlink等所有类型节点的属性数据,其中记录了如节点id,节点名称,访问权限,创建和访问时间等等信息;
2:name: “INODE_DIR" 整个目录树中所有节点之间的父子关系,配合INODE可构建完整的目录树;
3:name: “FILES_UNDERCONSTRUCTION" 尚未完成写入的文件集合,主要为重启时重建Lease集合;
4:name: “SNAPSHOT" 记录Snapshot数据,快照是Hadoop
2.1.0引入的新特性,用于数据备份、回滚,以防止因用户误操作导致集群出现数据问题;
5:name: “SNAPSHOT_DIFF" 执行快照操作的目录/文件的Diff集合数据,与SNAPSHOT一起构建较完整的快照管理能力;
6:name: “INODE_REFERENCE" 当目录/文件被操作处于快照,且该目录/文件被重命名后,会存在多条访问路径,INodeReference就是为了解决该问题;
7:name: “SECRET_MANAGER" 记录DelegationKey和DelegationToken数据,根据DelegationKey及由DelegationToken构造出的DelegationTokenIdentifier方便进一步计算密码,以上数据可以完善所有合法Token集合;
8:name: “CACHE_MANAGER" 集中式缓存特性全局信息,集中式缓存特性是Hadoop-2.3.0为提升数据读性能引入的新特性;
9:name: “STRING_TABLE" 字符串到id的映射表,维护目录/文件的Permission字符到ID的映射,节省存储空间;
接下去是summary区域,它主要标识了上面10个section区域各区域的name和在fsimage上的起始位置offset及各区域所占的长度length,此外还记录了fsimage的版本号ondiskVersion布局版本号layoutVersion及解压/压缩器codec
文件最后(占4个字节)记录的是summary信息所占空间的长度信息(通过file.seek(fileLength - FILE_LENGTH_FIELD_SIZE)获得);
)。