理解FSImage,首先需要理解INode。
下面是Hadoop2.6.0下的INode类图。


Hadoop2.6版本的FSImage结构解析
Hadoop2.6版本的FSImage结构解析
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有多个具体实现如下:
Hadoop2.6版本的FSImage结构解析
Hadoop2.6版本的FSImage结构解析

可以看出来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型保存了MODEGROUPUSER三个具体内容。

此外INodeWithAdditionalFields抽象类,在INode基础上对很多抽象方法进行了实现,主要还是权限相关和特性Feature及作为文件、目录软连接用的基础方法。

INodeFile类在INodeWithAdditionalFields基础上多了header属性和blocks属性,其中header同父类INodeWithAdditionalFieldspermission一样也是用一个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,用来记录层级关系,这样就构成了树状结构。文件夹相关的配额信息等都通过提供的方法写入了INodeWithAdditionalFieldsFeature上了。

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的结构就容易了。首先上图

Hadoop2.6版本的FSImage结构解析
Hadoop2.6版本的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)获得);
)。













相关文章:

  • 2021-11-12
  • 2022-01-05
  • 2021-04-07
  • 2022-12-23
  • 2022-12-23
  • 2021-07-02
猜你喜欢
  • 2021-10-07
  • 2021-08-08
  • 2022-01-11
  • 2021-10-08
  • 2022-12-23
  • 2021-08-17
  • 2021-08-23
相关资源
相似解决方案