FSImage文件是HDFS中名字节点NameNode上文件/目录元数据在特定某一时刻的持久化存储文件。它的作用不言而喻,在HA出现之前,NameNode因为各种原因宕机后,若要恢复或在其他机器上重启NameNode,重新组织元数据,就需要加载对应的FSImage文件、FSEditLog文件,并在内存中重做FSEditLog文件中的事务条目。本节,我们先来看下FSImage文件格式,及其内部数据是如何组织的。

通过翻看HDFS中加载FSImage文件的代码,从FSNamesystem的loadFSImage()方法开始,我将HDFS集群上的一个FSImage文件放到本地Windows系统中的F盘下,并写了如下方法解析文件,并打印关键内容,如下:

  1. import java.io.IOException;
  2. import java.io.File;
  3. import java.util.List;
  4. import org.junit.Test;
  5. import java.io.ByteArrayInputStream;
  6. import java.io.RandomAccessFile;
  7. import org.apache.hadoop.hdfs.server.namenode.FsImageProto.FileSummary;
  8. import org.apache.hadoop.hdfs.server.namenode.FsImageProto.FileSummary.Section;
  9. public class TestImageUtil {
  10. @Test
  11. public void testImage() {
  12. // 文件头字符串HDFSIMG1对应byte[]
  13. byte[] fileHead = "HDFSIMG1".getBytes();
  14. RandomAccessFile raFile = null;
  15. try {
  16. // 创建文件file,对应为f盘下FSImage文件fsimage_0000000000002311798
  17. File file = new File("f:/fsimage_0000000000002311798");
  18. raFile = new RandomAccessFile(file, "r");
  19. // 文件summary长度域所占大小为4
  20. final int FILE_LENGTH_FIELD_SIZE = 4;
  21. System.out.println("文件summary长度域大小:FILE_LENGTH_FIELD_SIZE=" + FILE_LENGTH_FIELD_SIZE);
  22. // 获取FSImage文件长度
  23. long fileLength = raFile.length();
  24. System.out.println("获取FSImage文件长度:fileLength=" + fileLength);
  25. // 创建文件头byte[]数组fileHeadTmp,用于存储文件头byte[]数组,大小为上述fileHead数组大小
  26. byte[] fileHeadTmp = new byte[fileHead.length];
  27. // 读入文件头至byte[]数组fileHeadTmp
  28. System.out.println("文件从头开始读取" + fileHeadTmp.length + "个byte至byte[]数组fileHeadTmp");
  29. raFile.readFully(fileHeadTmp);
  30. // 获取文件头长度
  31. System.out.println("获取文件头长度:fileHeadLength=" + fileHead.length);
  32. // 将byte[]数组fileHeadTmp转换成字符串fileHeadString
  33. String fileHeadString = new String(fileHeadTmp);
  34. // 验证文件头字符串
  35. System.out.println("fileHeadString=" + fileHeadString);
  36. // 文件file通过raFile.seek()方法定位到文件summary长度字段起始处,即文件大小减去文件summary长度域所占字节数4
  37. raFile.seek(fileLength - FILE_LENGTH_FIELD_SIZE);
  38. System.out.println("文件定位到文件summary长度开始处:" + (fileLength - FILE_LENGTH_FIELD_SIZE));
  39. // 读入一个int,即文件长度summaryLength
  40. int summaryLength = raFile.readInt();
  41. System.out.println("获取文件summary部分长度:summaryLength=" + summaryLength);
  42. // 文件file通过raFile.seek()方法定位到文件summary部分开始处,即文件大小减去文件长度所占字节数4,再减去文件内容总长度
  43. raFile.seek(fileLength - FILE_LENGTH_FIELD_SIZE - summaryLength);
  44. System.out.println("文件定位到文件summary部分开始处:" + (fileLength - FILE_LENGTH_FIELD_SIZE - summaryLength));
  45. // 再从当前位置开始读入文件summary部分内容
  46. // 构造文件长度summaryLength大小的byte[]数组
  47. byte[] summaryBytes = new byte[summaryLength];
  48. // 读取文件内容至数组summaryBytes
  49. raFile.readFully(summaryBytes);
  50. System.out.println("从当前位置开始读入文件summary部分内容至summaryBytes数组");
  51. FileSummary summary = FileSummary
  52. .parseDelimitedFrom(new ByteArrayInputStream(summaryBytes));
  53. System.out.println("解析文件summary部分内容如下:");
  54. System.out.println("1、ondiskVersion=" + summary.getOndiskVersion());
  55. System.out.println("2、layoutVersion=" + summary.getLayoutVersion());
  56. System.out.println("3、codec=" + summary.getCodec());
  57. System.out.println("4、section");
  58. List<Section> sectionsList = summary.getSectionsList();
  59. for (Section section : sectionsList) {
  60. System.out.println(" ");
  61. System.out.println("name=" + section.getName());
  62. System.out.println("length=" + section.getLength());
  63. System.out.println("offset=" + section.getOffset());
  64. }
  65. } catch (Exception e) {
  66. e.printStackTrace();
  67. } finally {
  68. if (raFile != null) {
  69. try {
  70. raFile.close();
  71. } catch (IOException e) {
  72. e.printStackTrace();
  73. }
  74. }
  75. }
  76. }
  77. /**
  78. * Supported section name. The order of the enum determines the order of
  79. * loading.
  80. */
  81. public enum SectionName {
  82. NS_INFO("NS_INFO"), STRING_TABLE("STRING_TABLE"), EXTENDED_ACL(
  83. "EXTENDED_ACL"), INODE("INODE"), INODE_REFERENCE(
  84. "INODE_REFERENCE"), SNAPSHOT("SNAPSHOT"), INODE_DIR("INODE_DIR"), FILES_UNDERCONSTRUCTION(
  85. "FILES_UNDERCONSTRUCTION"), SNAPSHOT_DIFF("SNAPSHOT_DIFF"), SECRET_MANAGER(
  86. "SECRET_MANAGER"), CACHE_MANAGER("CACHE_MANAGER");
  87. private static final SectionName[] values = SectionName.values();
  88. public static SectionName fromString(String name) {
  89. for (SectionName n : values) {
  90. if (n.name.equals(name))
  91. return n;
  92. }
  93. return null;
  94. }
  95. private final String name;
  96. private SectionName(String name) {
  97. this.name = name;
  98. }
  99. }
  100. }

关于代码解释,我们会在专门的FSImage文件加载源码分析相关文章中进行详细介绍,本文只关注FSImage文件的总体格式。

执行上述方法,打印内容输出如下:

  1. 文件summary长度域大小:FILE_LENGTH_FIELD_SIZE=4
  2. 获取FSImage文件长度:fileLength=1154156
  3. 文件从头开始读取8个byte至byte[]数组fileHeadTmp
  4. 获取文件头长度:fileHeadLength=8
  5. fileHeadString=HDFSIMG1
  6. 文件定位到文件summary长度开始处:1154152
  7. 获取文件summary部分长度:summaryLength=231
  8. 文件定位到文件summary部分开始处:1153921
  9. 从当前位置开始读入文件summary部分内容至summaryBytes数组
  10. 解析文件summary部分内容如下:
  11. 1、ondiskVersion=1
  12. 2、layoutVersion=-60
  13. 3、codec=
  14. 4、section
  15. name=NS_INFO
  16. length=27
  17. offset=8
  18. name=INODE
  19. length=1093067
  20. offset=35
  21. name=INODE_DIR
  22. length=60225
  23. offset=1093102
  24. name=FILES_UNDERCONSTRUCTION
  25. length=345
  26. offset=1153327
  27. name=SNAPSHOT
  28. length=68
  29. offset=1153672
  30. name=SNAPSHOT_DIFF
  31. length=36
  32. offset=1153740
  33. name=INODE_REFERENCE
  34. length=0
  35. offset=1153776
  36. name=SECRET_MANAGER
  37. length=9
  38. offset=1153776
  39. name=CACHE_MANAGER
  40. length=7
  41. offset=1153785
  42. name=STRING_TABLE
  43. length=129
  44. offset=1153792

不难看出,文件的总长度为1154156,这与我通过windows系统下右击-属性的方式查看结果是一致的,如下:


        (一)文件的起始位置(下标我们从0开始),0-7处为文件头信息,占8个byte的"HDFSIMG1";

(二)然后是接下来是10个section区域,这部分在FSImage文件中所占起止位置为8-1153920,这些是根据下面的summary区域的分析得到的结论,section分别如下:

1、8-34:占27个byte的section--NS_INFO,命名系统NameSystem信息section区域,具体内容后续文章再讲;

2、35-1093101:占1093067个byte的section--INODE,HDFS中INODE节点section区域,具体内容后续文章再讲;

3、1093102-1153326:占60225个byte的section--INODE_DIR,HDFS中INODE目录节点section区域,具体内容后续文章再讲;

4、1153327-1153671:占345个byte的section--FILES_UNDERCONSTRUCTION,HDFS中FILES_UNDERCONSTRUCTION处于构建状态文件部分section区域,具体内容后续文章再讲;

5、1153672-1153739:占68个byte的section--SNAPSHOT,HDFS中SNAPSHOT快照部分section区域,具体内容后续文章再讲;

6、1153740-1153775:占36个byte的section--SNAPSHOT_DIFF,HDFS中SNAPSHOT_DIFF部分section区域,具体内容后续文章再讲;

7、1153776-?:占0个byte的section--INODE_REFERENCE,HDFS中INODE_REFERENCE节点引用部分section区域,具体内容后续文章再讲,实际上本文件中没有这部分,为了体现FSImage文件的完整性,还是增加这部分的描述;

8、1153776-1153784:占9个byte的section--SECRET_MANAGER,HDFS中SECRET_MANAGER部分section区域,具体内容后续文章再讲;

9、1153785-1153791:占7个byte的section--CACHE_MANAGER,HDFS中CACHE_MANAGER部分section区域,具体内容后续文章再讲;

10、1153792-1153920:占129个byte的section--STRING_TABLE,HDFS中STRING_TABLE部分section区域,具体内容后续文章再讲;

(三)再接下来是文件summary区域,这部分在FSImage文件中所占起止位置为1153921-1154151,长度为231,它主要标识了上述各section区域的区域名name、在FSImage文件所占长度length及其起始位置offset,另外还有三个十分总要的变量,FSImage文件在磁盘上的版本号ondiskVersion、布局layout版本号layoutVersion及其解压/压缩器codec,前面两个会在load文件时与HDFS中NameNode进程内存中的版本号分别进行校验,防止错误版本的FSImage文件被加载,而codec则用于如何加载各个section区域,为空默认不做任何解压/压缩处理;

(四)最后为文件summary部分所占长度区域,这部分在FSImage文件中所占起止位置为1154152-1154155,正好是文件的最后一部分内容。

或许通过图的方式你会看的更直观,但是请原谅我拙劣的画图技巧:

实际上,FSImage文件中各个区域包含的内容,采用的是Google的protobuf编码格式,而protobuf不单单是一种消息传输格式,你也可以把它理解为一种数据编码格式,所以各个区域数据格式,在HDFS内的fsimage.proto文件中也有所阐述,比如FileSummary:

  1. message FileSummary {
  2. // The version of the above EBNF grammars.
  3. required uint32 ondiskVersion = 1;
  4. // layoutVersion describes which features are available in the
  5. // FSImage.
  6. required uint32 layoutVersion = 2;
  7. optional string codec         = 3;
  8. // index for each section
  9. message Section {
  10. optional string name = 1;
  11. optional uint64 length = 2;
  12. optional uint64 offset = 3;
  13. }
  14. repeated Section sections = 4;
  15. }

它就包含我们上面所描述的ondiskVersion、layoutVersion、codec、sections五部分,最后的sections是可以重复的,即repeated,而每个section又是一个message,包含name、length、offset三部分,正和我们上面解析的结果一致。

又如StringTableSection:

  1. /**
  2. * This section maps string to id
  3. * NAME: STRING_TABLE
  4. */
  5. message StringTableSection {
  6. message Entry {
  7. optional uint32 id = 1;
  8. optional string str = 2;
  9. }
  10. optional uint32 numEntry = 1;
  11. // repeated Entry
  12. }

包含两部分,Entry数量:numEntry,和重复的Entry,每个Entry又是一个Message,包含id和str两部分。

以上就是FSImage文件的主体信息,至于文件中的详细内容,特别是每个不同section区域中都有哪些内容,尤其是复杂的INodeSection等,我们后续再讲!

HDFS源码分析之FSImage文件内容(一)总体格式的更多相关文章

  1. HDFS源码分析数据块校验之DataBlockScanner

    DataBlockScanner是运行在数据节点DataNode上的一个后台线程.它为所有的块池管理块扫描.针对每个块池,一个BlockPoolSliceScanner对象将会被创建,其运行在一个单独 ...

  2. HDFS源码分析EditLog之读取操作符

    在<HDFS源码分析EditLog之获取编辑日志输入流>一文中,我们详细了解了如何获取编辑日志输入流EditLogInputStream.在我们得到编辑日志输入流后,是不是就该从输入流中获 ...

  3. HDFS源码分析之数据块及副本状态BlockUCState、ReplicaState

    关于数据块.副本的介绍,请参考文章<HDFS源码分析之数据块Block.副本Replica>. 一.数据块状态BlockUCState 数据块状态用枚举类BlockUCState来表示,代 ...

  4. HDFS源码分析EditLog之获取编辑日志输入流

    在<HDFS源码分析之EditLogTailer>一文中,我们详细了解了编辑日志跟踪器EditLogTailer的实现,介绍了其内部编辑日志追踪线程EditLogTailerThread的 ...

  5. HDFS源码分析心跳汇报之数据块汇报

    在<HDFS源码分析心跳汇报之数据块增量汇报>一文中,我们详细介绍了数据块增量汇报的内容,了解到它是时间间隔更长的正常数据块汇报周期内一个smaller的数据块汇报,它负责将DataNod ...

  6. Yii2.0源码分析之——控制器文件分析(Controller.php)创建动作、执行动作

    在Yii中,当请求一个Url的时候,首先在application中获取request信息,然后由request通过urlManager解析出route,再在Module中根据route来创建contr ...

  7. HDFS源码分析之UnderReplicatedBlocks(一)

    http://blog.csdn.net/lipeng_bigdata/article/details/51160359 UnderReplicatedBlocks是HDFS中关于块复制的一个重要数据 ...

  8. HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

    HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

  9. HDFS源码分析数据块复制监控线程ReplicationMonitor(一)

    ReplicationMonitor是HDFS中关于数据块复制的监控线程,它的主要作用就是计算DataNode工作,并将复制请求超时的块重新加入到待调度队列.其定义及作为线程核心的run()方法如下: ...

随机推荐

  1. 宠物收养所 (SBT)

    宠物收养所 最近,阿Q开了一间宠物收养所.收养所提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物.每个领养者都希望领养到自己满意的宠物,阿Q根据领养者的要求通过他自己发明的一个特殊的公式,得 ...

  2. Gmail进程信息转储分析工具pdgmail

    Gmail进程信息转储分析工具pdgmail   进程信息转储(Process Memory Dump)是数字取证的重要方式.通过分析对应进程的信息转储,可以获取大量的信息.Kali Linux提供一 ...

  3. 固件分析工具Binwalk

    固件分析工具Binwalk   固件是保存在嵌入式设备存储器的程序.它负责设备运行和功能,如路由器等设备.通过分析固件文件,可以了解设备的工作方式,并且确认是否存在漏洞.Binwalk是Kali Li ...

  4. bmi健康指数

    #!/usr/bin/python # -*- coding: utf-8 -*- #小明身高1.75,体重80.5kg.请根据BMI公式(体重除以身高的平方) #帮小明计算他的BMI指数,并根据BM ...

  5. iOS开发 Swift开发数独游戏(四) 游戏界面的界面与逻辑

    一.游戏界面涉及到的功能点 1)数独格子的建模 (1)绘制数独格子要考虑到标记功能 所以要在每个格子内预先塞入9个标记数字,仅数独格子算下来就有9*9*9=729个格子且存在大量嵌套(这导致我在操作S ...

  6. Jackson的高级应用(转)

    Jackson 是当前用的比较广泛的,用来序列化和反序列化 JSON 的 Java 的开源框架.Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson ...

  7. 决定干点事儿--翻译一下《effective modern c++》

    写了非常多关于C++11的博客.总是认为不踏实,非常多东西都是东拼西凑.市场上也非常少有C++11的优秀书籍,但幸运的是Meyers老爷子并没有闲赋.为我们带来了<effective moder ...

  8. Linux下查找、删除、替换命令

    查看某目录下所有文件的个数: [root@localhost1 opt]# ls -l |grep "^-"|wc -l 查看某目录下所有文件的个数,包括子目录里面的: [root ...

  9. Lock flag DX

    https://msdn.microsoft.com/en-us/library/windows/desktop/bb322846(v=vs.85).aspx discard nooverwrite ...

  10. 基于Redis的Bloomfilter去重(转载)

    转载:http://blog.csdn.net/bone_ace/article/details/53107018 前言 “去重”是日常工作中会经常用到的一项技能,在爬虫领域更是常用,并且规模一般都比 ...