这里,对重要的FSImage类进行阅读分析。
该类的继承层次关系如下所示:
我们一个一个地分析:
- StorageInfo类
该类是一个存储信息的通用实体类, 该类定义了如下三个属性:
其中,namespaceID表示的是文件系统命名空间的存储ID,它的生成基于Hash算法的,根据address + port来进行散列得到的。
- Storage类
该类继承自StorageInfo,是一个抽象类。
本地存储信息被存储在一个单独的文件VERSION中,它包含结点的类型、存储布局(Layout)版本、 文件系统命名空间ID、文件系统状态的创建时间。本地存储可以分布在多个目录中,每个目录中应该保存着相同的VERSION文件,当Hadoop servers (name-node and data-nodes)启动的时候从这些VERSION文件中读取本地的存储信息。当Hadoop servers启动的时候,会对每个存储目录都持有一把锁,这是为了使得其他的结点不能因为共享同一个存储目录而启动。当Hadoop servers停止的时候,会释放掉它们所持有的锁,以便其它结点来使用该存储目录。
首先,看存储状态的枚举定义:
下面是与存储相关的几个文件或目录:
我们再看一下,该类中定义的3个与存储相关的内部类:
1、StorageDirType接口类
该接口定义了与存储目录的类型相关的方法:
Storage类中,并没有实现该存储目录类型的内部类。
2、StorageDirectory类:
该类表示一个单独的存储目录的实体类,它定义了如下三个属性:
前面提到,每个存储对应一个VERSION文件,也就是在每个存储目录中保存。
StorageDirectory类提供了对VERSION文件的读写操作,而且还给出了对一个存储目录进行一致性检查的操作analyzeStorage:根据Hadoop servers启动的选项StartupOption,来返回不正常的存储状态,如果正常启动则检查上面与存储相关的文件或目录是否存在,并返回对应的存储状态。
该类中定义的doRecover方法,能够从上一个失败的事务中来完成或恢复存储状态,主要包括对下述状态进行恢复操作:
COMPLETE_UPGRADE: 将previous.tmp重命名为previous
RECOVER_UPGRADE: 将previous.tmp重命名为current
COMPLETE_ROLLBACK: 删除removed.tmp
RECOVER_ROLLBACK: 将removed.tmp重命名为current
COMPLETE_CHECKPOINT: 将lastcheckpoint.tmp重命名为 previous.checkpoint
RECOVER_CHECKPOINT: 将lastcheckpoint.tmp重命名为current
另外,StorageDirectory类能够控制对当前存储进行加锁lock、解锁unlock、尝试获取锁tryLock的操作。
3、DirIterator类
该类是StorageDirectory的迭代器类,用来方便地迭代出一个StorageDirectory列表中的StorageDirectory存储目录实例。
下面,继续看Storage类的实现。
用来描述一个存储的信息,应该包括使用该存储的结点(通过结点类型来标识) 、该存储包含的目录,如下所示:
基本上,在Storage类中实现的操作都是与存储目录相关的,比如,获取到一个StorageDirectory列表的迭代器实例、删除目录等等,可以查看该类的具体实现。
- FsImage类
首先,看该类中定义的三个枚举类:
再看一个内部类DatanodeImage,该类用于将Datanode的持久化信息存储到FsImage映像中,实现如下所示:
对FSImage类源代码的阅读,我们从构造一个FSImage实例的方法来看,如下所示:
可见,FSImage实例对应着一个特定的为Namenode而创建的存储实例,该存储实例的内容,实际上维护着一个目录列表,在构造FSImage实例的过程中,全部加载并初始化。
接着,介绍FSImage类的重要方法。
1、加载FsImage映像文件
对应的实现方法为loadFSImage,该类实现了两个重载的加载映像文件的方法。
首先看第一个带参数的loadFSImage方法,从一个文件中加载映像到文件系统中,实现如下所示:
再看第二个不带参数的loadFSImage方法,它从一个目录中选择最新的映像文件并加载到内存中,同时与这个目录中的EditLog日志文件合并如下所示:
2、从一个中断的检查点恢复加载映像文件
该方法recoverInterruptedCheckpoint的实现如下所示:
通过上面可以看出,检查点进程所执行的操作是对fsimage映像文件进行检查,从而生成一个fsimage.ckpt文件。而很可能会(只要fsimage.ckpt文件存在)从一个fsimage.ckpt文件来加载fsimage映像文件,在Namenode停止的时候,将fsimage.ckpt重命名为fsimage完成此次检查点进程的执行,并把edits.new重命名为edits。
3、切换映像
实现方法rollFSImage,能够将fsimage.ckpt重命名为fsImage,将edits.new重命名为edits,并打开一个空的edits文件。实现如下所示:
4、保存映像文件
实现方法为saveFSImage,存在两个重载的方法。
第一个是带参数的方法,将fsimage.new或fsimage文件的内容进行保存。如下所示:
上面调用saveImage方法,实际上方法循环调用了saveINode2Image来写入fsDir.rootDir目录树的属性信息的,可以参考这两个方法的实现。
上面方法是将一些必要的信息都序列化到了指定的映像文件中,作为Namenode掌握当前HDFS集群中重要信息的内存映像。
第二个是不带参数的该方法,保存fsimage.ckpt文件的内容,并创建一个新的edits文件。实现如下所示:
5、 从检查点目录加载fsimage映像文件
实现的方法为doImportCheckpoint,如下所示:
6、初始化分布式升级
主要通过获取到UpgradeManagerNamenode 升级管理器,判断是否进行了升级之前的初始化工作,只有做好初始化工作,才开始对fsimage映像文件进行初始化。
实现方法initializeDistributedUpgrade如下所示:
7、执行升级
实现方法为doUpgrade,如下所示:
8、执行升级后的后继清理
当执行升级的时候,可能需要执行目录的重命名,或者删除一些临时文件目录,对指定的StorageDirectory目录执行清理工作的方法为doFinalize,如下所示:
上面的方法比较容易。如果想要执行批量清理,调用方法finalizeUpgrade可以实现,如下所示:
该方法对全部的目录执行清理工作。
9、回滚操作
执行回滚操作时有条件的,如果文件系统不存在一个先前的状态,是不能够执行回滚操作的,因为根本无法将当前的fsimag改变成上一个状态。另外,文件系统先前的一个状态的信息保存在一个或多个存储目录中,获取到这些先前的状态信息才能够执行回滚操作。而对于不存在文件系统状态信息的存储目录是不需要回滚的。
实现回滚操作的方法为doRollback,如下所示:
可见,回滚操作就是,在保证全部目录一致性检查通过的情况下,将当前存在的previous目录重命名为current目录,表示将previous目录状态覆盖到current目录上,然后删除被覆盖掉的current目录(通过先重命名current为removed.tmp,然后执行删除)。
10、格式化操作
对文件系统中的StorageDirectory存储目录进行格式化操作,存在两个重载的方法,分别介绍如下。
第一个带参数的方法,是对指定的存储目录进行格式化,如下所示:
比较容易理解。
第二个不带参数的方法,是批量进行格式化,如下所示:
11、加载Datanode信息
事实上,对于新版本的Hadoop已经不再使用Datanode的映像了,该方法是为了兼容旧版本的,如下所示:
简单做个总结:
FSImage类是与fsimage相关的,主要保存了文件系统的状态信息。其中,在加载该映像到内存中的时候,相关的日志文件是edits,edits总是保存当前对文件系统执行操作的最新记录,根据需要启动检查点进程来将edits事务日志记录作用于fsimage映像上,从而系统通过fsimage将发生的事务作用在文件系统实例所维护的存储目录上。
在对fsimage操作的过程中,相关的文件主要包括fsimage、fsimage.ckpt、edits、edits.new这四个,而且在适当的时候,启动检查点进程对内存中映像fsimage执行同步操作。