前面我们提过关系:文件名à数据块持久化在磁盘上,所有对目录树的更新和文件名à数据块关系的修改,都必须能够持久化。为了保证每次修改不需要从新保存整个结构,HDFS使用操作日志,保存更新。
现在我们可以得到NameNode需要存储在Disk上的信息了,包括:
[hadoop@localhostdfs]$ ls -R name
name:
current image in_use.lock
name/current:
edits fsimage fstime VERSION
name/image:
fsimage
in_use.lock的功能和DataNode的一致。fsimage保存的是文件系统的目录树,edits则是文件树上的操作日志,fstime是上一次新打开一个操作日志的时间(long型)。
image/fsimage是一个保护文件,防止0.13以前的版本启动(0.13以前版本将fsimage存放在name/image目录下,如果用0.13版本启动,显然在读fsimage会出错J)。
我们可以开始讨论FSImage了,类FSImage如下图:
分析FSImage,不免要跟DataStorage去做比较(上图也保留了类DataStorage)。前面我们已经分析过DataStorage的状态变化,包括升级/回滚/提交,FSImage也有类似的升级/回滚/提交动作,而且这部分的行为和DataStorage是比较一致,如下状态转移图。图中update方法和DataStorage的差别比较大,是因为处理数据库和处理文件系统名字空间不一样,其他的地方都比较一致。FSImage也能够管理多个Storage,而且还能够区分Storage为IMAGE(目录结构)/EDITS(日志)/IMAGE_AND_EDITS(前面两种的组合)。
我们可以看到,FSImage和DataStorage都有recoverTransitionRead方法。FSImage的recoverTransitionRead方法主要步骤是检查系统一致性(analyzeStorage)并尝试恢复,初始化新的storage,然后根据启动NameNode的参数,做升级/回滚等操作。
FSImage需要支持参数-importCheckpoint,该参数用于在某一个checkpoint目录里加载HDFS的目录信息,并更新到当前系统,该参数的主要功能在方法doImportCheckpoint中。该方法很简单,通过读取配置的checkpoint目录来加载fsimage文件和日志文件,然后利用saveFSImage(下面讨论)保存到当前的工作目录,完成导入。
loadFSImage(File curFile)用于在fsimage中读入NameNode持久化的信息,是FSImage中最重要的方法之一,该文件的结构如下:
最开始是版本号(注意,各版本文件布局不一样,文中分析的样本是0.17的),然后是命名空间的ID号,文件个数和最高文件版本号(就是说,下一次产生文件版本号的初始值)。接下来就是文件的信息啦,首先是文件名,然后是该文件的副本数,接下来是修改时间/访问时间,数据块大小,数据块数目。数据块数目如果大于0,表明这是个文件,那么接下来就是numBlocks个数据块(浅蓝),如果数据块数目等于0,那该条目是目录,接下来是应用于该目录的quota。最后是访问控制的一些信息。文件信息一共有numFiles个,接下来是处于构造状态的文件的信息。(有些版本可能还会保留DataNode的信息,但0.17已经不保存这样的信息啦)。loadFSImage(File curFile)的对应方法是saveFSImage(FilenewFile),FSImage中还有一系列的方法(大概7,8个)用于配合这两个方法工作,我们就不再深入讨论了。
loadFSEdits(StorageDirectory sd)用于加载日志文件,并把日志文件记录的内容应用到NameNode,loadFSEdits只是简单地调用FSEditLog中对应的方法。
loadFSImage()和saveFSImage()是另外一对重要的方法。
loadFSImage()会在所有的Storage中,读取最新的NameNode持久化信息,并应用相应的日志,当loadFSImage()调用返回以后,内存中的目录树就是最新的。loadFSImage()会返回一个标记,如果Storage中有任何和内存中最终目录树中不一致的Image(最常见的情况是日志文件不为空,那么,内存中的Image应该是Storage的Image加上日志,当然还有其它情况),那么,该标记为true。
saveFSImage()的功能正好相反,它将内存中的目录树持久化,很自然,目录树持久化后就可以把日志清空。saveFSImage()会创建edits.new,并把当前内存中的目录树持久化到fsimage.ckpt(fsimage现在还存在),然后重新打开日志文件edits和edits.new,这会导致日志文件edits和edits.new被清空。最后,saveFSImage()调用rollFSImage()方法。
rollFSImage()上来就把所有的edits.new都改为edits(经过了方法saveFSImage,它们都已经为空),然后再把fsimage.ckpt改为fsimage。如下图:
为了防止误调用rollFSImage(),系统引入了状态CheckpointStates.UPLOAD_DONE。
有了上面的状态转移图,我们就很好理解方法recoverInterruptedCheckpoint了。
图中存在另一条路径,应用于GetImageServlet中。GetImageServlet是和从NameNode进行文件通信的接口,这个场景留到我们分析从NameNode时再进行分析。
最后我们分析一下和检查点相关的一个类,rollFSImage()会返回这个类的一个实例。CheckpointSignature用于标识一个日志的检查点,它是StorageInfo的子类,同时实现了WritableComparable接口,出了StorageInfo的信息,它还包括了两个属性:editsTime和checkpointTime。editsTime是日志的最后修改时间,checkpointTime是日志建立时间。在和从NameNode节点的通信中,需要用CheckpointSignature,来保证从NameNode获得的日志是最新的。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。