Namenode 最重要的两个功能之一就是维护文件系统的命名空间 NameSpace

HDFS 文件系统的命名空间在 Namenode 的内存中是以树的结构来存储的。HDFS文件系统的命名空间是以/为根的整个目录树,是通过 FSDirectory 类来管理的。在 HDFS 中,不管是目录还是文件,在文件系统目录树中都被看作是一个 INode 节点:如果是目录,则其对应的类为 INodeDirectory;如果是文件,则其对应的类为INodeFile

INodeDirectory 以及 INodeFile 类都是 INode 的派生类。INodeDirectory 中包含一个成员集合变量 children,如果该目录下有子目录或者文件, 其子目录或文件的 INode 引用就会被保存在 children 集合中。HDFS 就是通过这种方式来维护整个文件系统的目录结构的

HDFS 会将命名空间保存到 Namenode 的本地文件系统上一个叫 fsimage(命名空间镜像) 的文件中。利用这个文件,Namenode 每次重启时都能将整个 HDFS 的命名空间重构,fsimage 文件的操作由 FSImage 类负责。另外,对 HDFS 的各种操作,Namenode 都会在操作日志中进行记录,以便周期性地将该日志与fsimage 进行合并生成新的 fsimage。该日志文件也在 Namenode 的本地文件系统中保存,叫editlog文件,editlog 的相关操作由FSEditLog 类管理

image.png

Element继承自Comparable,Inode实现了部分Element的方法

INodeDirectory 和 INodeFiled 都是抽象类 INodeWithAdditionalFields 的实现类。而 INodeWithAdditionalFields的父类为 INode,首先从 INode 抽象类开始说起

INode抽象类

INode 类是整个 INode 体系的根接口,它是一个抽象类,保存了 HDFS 目录和文件的所有共同属性,包括当前节点的父节点的 INode 对象的引用(只能是 INodeDirectory 类或者 INodeReference 类)、文件/目录名、用户组、访问权限、最后修改时间、上次访问时间、完整路径名、文件扩展属性等
image.png

字段

INode 类中只有一个字段,就是 parent,表明当前 INode 的父目录。HDFS 中除了根目录外,其他所有的文件与目录都存在一个父目录。父目录的类型只能是 INodeDirectory 类或者 INodeReference 类之一

  1. private INode parent = null;

构造方法

INode 构造方法只有一个参数就是传入 INode 类型的对象,类型只能是 INodeDirectory 类或者 INodeReference类之一

  1. INode(INode parent) {
  2. this.parent = parent;
  3. }

INode元信息方法

id:INode的id fullPathName:文件/目录的完整路径 parent: 文件/目录的父节点

INode还提供了如下几个基本的判断方法 isFile():判断是否为文件 isDirectory():判断是否为目录 isSymlink():判断是否为符号链接 isRoot():判断是否为文件系统目录树的根节点

INodeAttributes接口

方法名 返回类型 描述
isDirectory boolean 是否是目录
getLocalNameBytes byte[] 返回用户名(byte [] 类型)
getUserName String 文件/目录所属用户名。
getGroupName String 文件/目录所属组名。
getFsPermission FsPermission 返回权限 (FsPermission 类型)
getFsPermissionShort short 返回权限(short 类型)
getPermissionLong long 返回权限(long 类型)
getAclFeature AclFeature 安全相关
getXAttrFeature





XAttrFeature 当前文件/目录的扩展属性

文件系统扩展属性是目前流行的POSIX系统中文件系统具有的一项特殊功能, 可以给文件、 文件夹添加额外的key/value键值对, 键和值都是字符串并且有一定长度的限制。 文件系统扩展属性使得现有的文件系统得以支持在原始设计中未提供的功能
getModificationTime getModificationTime 文件/目录上次修改时间
getAccessTime getAccessTime 文件/目录上次访问时间

抽象类INodeWithAdditionalFields

INode是一个根接口类,但是 INode 类只定义了一个字段 parent ,其余字段的值都是通过抽象的 get() 方法获得的,并且留给了子类来定义

INodeWithAdditionalFields 里面定义了具体的属性字段,比如:id、name、permission、modificationTime、 accessTime等

permission 字段是 long 类型的,其中前16个比特用来存放文件权限标识(mode,类似于Linux中的777),中间24个比特用来存放用户组标识(group),最后24个比特用来存放用户名标识(user)

在 HDFS 中,用户名和用户标识的对应关系、用户组名和用户组标识的对应关系都保存在 SerialNumberManager类中。通过 SerialNumberManager 类,名字节点不必在 INode 对象中保存字符串形式的用户名和用户组名, 只需将整型的用户名标识和用户组名标识放入 permission 字段中即可

  1. enum PermissionStatusFormat implements LongBitFormat.Enum {
  2. MODE(null, 16),
  3. GROUP(MODE.BITS, 24),
  4. USER(GROUP.BITS, 24);
  5. final LongBitFormat BITS;
  6. private PermissionStatusFormat(LongBitFormat previous, int length) {
  7. BITS = new LongBitFormat(name(), previous, length, 0);
  8. }
  9. // 提取最后24个比特的user信息,并通过SerialNumberManager获取用户名
  10. static String getUser(long permission) {
  11. }
  12. // 提取中间24个比特的group信息,并通过SerialNumberManager获取用户组名
  13. static String getGroup(long permission) {
  14. }
  15. // 提取前16个比特的mode信息
  16. static short getMode(long permission) {
  17. }
  18. /**
  19. * 将一个PermissionStatus类,转换成long类型的permission信息
  20. *
  21. * Encode the {@link PermissionStatus} to a long. */
  22. static long toLong(PermissionStatus ps) {
  23. }
  24. static PermissionStatus toPermissionStatus(long id,
  25. SerialNumberManager.StringTable stringTable) {
  26. }
  27. @Override
  28. public int getLength() {
  29. return BITS.getLength();
  30. }
  31. }


INodeDirectory类

INodeDirectory 抽象了 HDFS 文件系统中的目录,目录是文件系统中的一个虚拟容器,里面保存了一组文件和其他一些目录。在 INodeDirectory 的实现中,添加了成员变量 children,用来保存目录中所有子目录项的 INode 对象

  1. // 使用一个 children 字段保存该目录中所有孩子节点的 INode 对象: ArrayList实例
  2. private List<INode> children = null;
  • children 字段的增、删、改、查方法:用于向当前目录添加、删除、替换、查找子目录项等操作,包括addChild()、removeChild()、replaceChild()、getChild()、clearChildren()、cleanSubtreeRecursively()等方法

  • 特性相关方法:用于向当前 INodeDirectory 添加新的 Feature 对象,以及获取指定 Feature 对象的方法,包括addDirectoryWithQuotaFeature()、getDirectoryWithQuotaFeature()、addSnapshottableFeature()、addSnapshotFeature()、getDirectorySnapshottableFeature()、getDirectoryWithSnapshotFeature()等方法

  • 快照相关方法: 用于向当前目录添加、删除或者更改快照等操作,包括isSnapshottable()、getSnapshot()、 setSnapshotQuota()、addSnapshot()、removeSnapshot()等方法

INodeFile类

在文件系统目录树中,使用 INodeFile 类抽象一个 HDFS 文件,INodeFile 类继承自 INodeWithAdditionalFields 类

INodeFile 类中保存了 HDFS 文件最重要的两个信息:文件头 header 字段和文件对应的数据块信息 blocks 字段。Header 字段保存了当前文件有多少个副本,以及文件数据块的大小(header字段的处理类似于 INode 中的permission字段,前4个比特用于保存存储策略,中间12个比特用于保存文件备份系数:1 + 11(最高的1位来确定该块是副本还是擦除编码,0副本,1擦除编码。对于复制块,尾11位存储复制因子。对于擦除编码块,尾11位存储EC策略ID,后48个比特用于保存数据块大小。使用内部类 HeaderFormat 处理);blocks字段是一个 BlockInfo类型的数组,保存了当前文件对应的所有数据块信息

  1. // 文件头信息
  2. // header字段保存了当前文件有多少个副本,以及文件数据块的大小
  3. // header字段的处理类似于INode中的permission字段,
  4. // 前4个比特用于保存存储策略,
  5. // 中间12个比特用于保存文件备份系数,
  6. // 后48个比特用于保存数据块大小。
  7. // 使用内部类HeaderFormat处理
  8. private long header = 0L;
  9. // 文件数据块信息
  10. // 文件对应的数据块信息blocks字段
  11. private BlockInfo[] blocks;
  1. INodeFile(long id, byte[] name, PermissionStatus permissions, long mtime,
  2. long atime, BlockInfo[] blklist, Short replication, Byte ecPolicyID,
  3. long preferredBlockSize, byte storagePolicyID, BlockType blockType) {
  4. super(id, name, permissions, mtime, atime);
  5. final long layoutRedundancy = HeaderFormat.getBlockLayoutRedundancy(
  6. blockType, replication, ecPolicyID);
  7. // 构建头信息
  8. header = HeaderFormat.toLong(preferredBlockSize, layoutRedundancy,
  9. storagePolicyID);
  10. // 设置blocks信息
  11. if (blklist != null && blklist.length > 0) {
  12. for (BlockInfo b : blklist) {
  13. Preconditions.checkArgument(b.getBlockType() == blockType);
  14. }
  15. }
  16. setBlocks(blklist);
  17. }


INodeReference类

INodeReference 是一个抽象类,继承 INode 抽象类。当HDFS文件/目录处于某个快照中,并且这个文件/目录被重命名或者移动到其他路径时,该文件/ 目录就会存在多条访问路径。WithName、WithCount、 DstReference 都是 INodeReference 的子类,同时也是 INodeReference 的内部类

WithName 对象用于替代重命名操作前源路径中的INode对象,DstReference 对象则用于替代重命名操作后目标路径中的INode对象。WithName 和 DstReference 同指向了一个 WithCount 对象,WithCount 对象则指向了文件系统目录树中真正的 INode 对象

此类及其子类用于支持多个访问路径 当文件/目录存储在某些快照中并重命名/移动到其他位置时,它可能具有多个访问路径

(1)假设我们有/abc/foo,假设foo的inode是inode(id=1000,name=foo)

(2)为/abc创建快照s0

(3)mv/abc/foo/xyz/bar,即inode(id=1000,name=…)将“foo”重命名到“bar”,它的父对象变成/xyz。

然后,/xyz/bar和/abc/.snapshot/s0/foo是指向同一inode,inode(id=1000,name=bar)的两条不同的访问路径。

关于references,我们可以得到以下信息:

  1. /abc 有一个子引用 (id : 1001,name: foo)
  2. /xyz 有一个子引用 (id : 1002)
  3. 引用(id : 1001,name: foo) 和引用(id : 1002) 指向同一个引用 (id:1003, count: 2)
  4. 最后(id:1003, count: 2) 执行inode (id:1000,name: bar)

注意:

  1. 对于没有名称的引用,例如ref(id=1002),它使用被引用inode的名称。
  2. getParent()总是返回当前状态的父对象,例如 : inode(id:1000,name:bar.getParent()返回/xyz而不是/abc