分享

Hadoop二次开发必备,Hadoop源码分析(八)

本帖最后由 pig2 于 2014-1-16 00:11 编辑

前面我们提过关系:文件名à数据块持久化在磁盘上,所有对目录树的更新和文件名à数据块关系的修改,都必须能够持久化。为了保证每次修改不需要从新保存整个结构,HDFS使用操作日志,保存更新。
现在我们可以得到NameNode需要存储在Disk上的信息了,包括:
  1. [hadoop@localhost dfs]$ ls -R name
  2. name:
  3. current  image  in_use.lock
  4. name/current:
  5. edits  fsimage  fstime  VERSION
  6. name/image:
  7. fsimage
复制代码
in_use.lock的功能和DataNode的一致。fsimage保存的是文件系统的目录树,edits则是文件树上的操作日志,fstime是上一次新打开一个操作日志的时间(long型)。image/fsimage是一个保护文件,防止0.13以前的版本启动(0.13以前版本将fsimage存放在name/image目录下,如果用0.13版本启动,显然在读fsimage会出错J)。我们可以开始讨论FSImage了,类FSImage如下图:
7.PNG

分析FSImage,不免要跟DataStorage去做比较(上图也保留了类DataStorage)。前面我们已经分析过DataStorage的状态变化,包括升级/回滚/提交,FSImage也有类似的升级/回滚/提交动作,而且这部分的行为和DataStorage是比较一致,如下状态转移图。图中update方法和DataStorage的差别比较大,是因为处理数据库和处理文件系统名字空间不一样,其他的地方都比较一致。FSImage也能够管理多个Storage,而且还能够区分Storage为IMAGE(目录结构)/EDITS(日志)/IMAGE_AND_EDITS(前面两种的组合)。
8.PNG

我们可以看到,FSImage和DataStorage都有recoverTransitionRead方法。FSImage的recoverTransitionRead方法主要步骤是检查系统一致性(analyzeStorage)并尝试恢复,初始化新的storage,然后根据启动NameNode的参数,做升级/回滚等操作。FSImage需要支持参数-importCheckpoint,该参数用于在某一个checkpoint目录里加载HDFS的目录信息,并更新到当前系统,该参数的主要功能在方法doImportCheckpoint中。该方法很简单,通过读取配置的checkpoint目录来加载fsimage文件和日志文件,然后利用saveFSImage(下面讨论)保存到当前的工作目录,完成导入。loadFSImage(File curFile)用于在fsimage中读入NameNode持久化的信息,是FSImage中最重要的方法之一,该文件的结构如下:
9.PNG

最开始是版本号(注意,各版本文件布局不一样,文中分析的样本是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。如下图:
10.PNG

为了防止误调用rollFSImage(),系统引入了状态CheckpointStates.UPLOAD_DONE。

有了上面的状态转移图,我们就很好理解方法recoverInterruptedCheckpoint了。

图中存在另一条路径,应用于GetImageServlet中。GetImageServlet是和从NameNode进行文件通信的接口,这个场景留到我们分析从NameNode时再进行分析。

最后我们分析一下和检查点相关的一个类,rollFSImage()会返回这个类的一个实例。CheckpointSignature用于标识一个日志的检查点,它是StorageInfo的子类,同时实现了WritableComparable接口,出了StorageInfo的信息,它还包括了两个属性:editsTime和checkpointTime。editsTime是日志的最后修改时间,checkpointTime是日志建立时间。在和从NameNode节点的通信中,需要用CheckpointSignature,来保证从NameNode获得的日志是最新的。

---------------------------------------------------------------------------------------------------------------------------------------------------

我们来分析FSEditLog.java,该类提供了NameNode操作日志和日志文件的相关方法,相关类图如下:
11.PNG

首先是FSEditLog依赖的输入/输出流。输入流基本上没有新添加功能;输出流在打开的时候,会写入日志的版本号(最前面的4字节),同时,每次将内存刷到硬盘时,会为日志尾部写入一个特殊的标识(OP_INVALID)。

FSEditLog有打开/关闭的方法,它们都是很简单的方法,就是关闭的时候,要等待所有正在写日志的操作都完成写以后,才能关闭。processIOError用于处理IO出错,一般这会导致对于的Storage的日志文件被关闭(还记得loadFSImage要找出最后写的日志文件吧,这也是提高系统可靠性的一个方法),如果系统再也找不到可用的日志文件,NameNode将会退出。

loadFSEdits是个大家伙,它读取日志文件,并把日志应用到内存中的目录结构中。这家伙大是因为它需要处理所有类型的日志记录,其实就一大case语句。logEdit的作用和loadFSEdits相反,它向日志文件中写入日志记录。我们来分析一下什么操作需要写log,还有就是需要log那些参数:logOpenFile(OP_ADD):申请lease
    path(路径)/replication(副本数,文本形式)/modificationTime(修改时间,文本形式)/accessTime(访问时间,文本形式)/preferredBlockSize(块大小,文本形式)/BlockInfo[](增强的数据块信息,数组)/permissionStatus(访问控制信息)/clientName(客户名)/clientMachine(客户机器名)


logCloseFile(OP_CLOSE):归还lease

path/replication/modificationTime/accessTime/preferredBlockSize/BlockInfo[]/permissionStatus


logMkDir(OP_MKDIR):创建目录

path/modificationTime/accessTime/permissionStatus


logRename(OP_RENAME):改文件名

src(原文件名)/dst(新文件名)/timestamp(时间戳)


logSetReplication(OP_SET_REPLICATION):更改副本数src/replicationlogSetQuota(OP_SET_QUOTA):设置空间额度

path/nsQuota(文件空间额度)/dsQuota(磁盘空间额度)


logSetPermissions(OP_SET_PERMISSIONS):设置文件权限位

src/permissionStatus


logSetOwner(OP_SET_OWNER):设置文件组和主

src/username(所有者)/groupname(所在组)


logDelete(OP_DELETE):删除文件

src/timestamp


logGenerationStamp(OP_SET_GENSTAMP):文件版本序列号

genstamp(序列号)


logTimes(OP_TIMES):更改文件更新/访问时间

src/modificationTime/accessTime

通过上面的分析,我们应该清楚日志文件里记录了那些信息。rollEditLog()我们在前面已经提到过(配合saveFSImage和rollFSImage),它用于关闭edits,打开日志到edits.new。purgeEditLog()的作用正好相反,它删除老的edits文件,然后把edits.new改名为edits。这也是Hadoop在做更新修改时经常采用的策略。
下一篇


上一篇


没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条