在安装 hadoop 的时候,必须对 hdfs 的 Namenode 进行格式化操作。本文主要介绍格式化操作

格式化命令如下

  1. bin/hdfs namenode -format

入口

Namenode 的格式化操作是:org.apache.hadoop.hdfs.server.namenode.NameNode

  1. /**
  2. */
  3. public static void main(String argv[]) throws Exception {
  4. //参数校验
  5. if (DFSUtil.parseHelpArgument(argv, NameNode.USAGE, System.out, true)) {
  6. System.exit(0);
  7. }
  8. try {
  9. StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);
  10. //调用createNameNode()方法创建NameNode对象
  11. NameNode namenode = createNameNode(argv, null);
  12. if (namenode != null) {
  13. //等待Namenode RPC服务结束
  14. namenode.join();
  15. }
  16. } catch (Throwable e) {
  17. LOG.error("Failed to start namenode.", e);
  18. //出现异常则直接退出执行
  19. terminate(1, e);
  20. }
  21. }

通过 main 入口的方法,我们可以知道接下来是调用 createNameNode 方法。然后根据传入的参数不同执行不同的代码片段。我们主要是看格式化操作,所以关注一下 FORMAT 这个代码片段就可以了

  1. public static NameNode createNameNode(String argv[], Configuration conf)
  2. throws IOException {
  3. LOG.info("createNameNode " + Arrays.asList(argv));
  4. //构建配置文件
  5. if (conf == null)
  6. conf = new HdfsConfiguration();
  7. // Parse out some generic args into Configuration.
  8. GenericOptionsParser hParser = new GenericOptionsParser(conf, argv);
  9. argv = hParser.getRemainingArgs();
  10. // Parse the rest, NN specific args.
  11. // 解析命令行的参数
  12. StartupOption startOpt = parseArguments(argv);
  13. if (startOpt == null) {
  14. printUsage(System.err);
  15. return null;
  16. }
  17. setStartupOption(conf, startOpt);
  18. boolean aborted = false;
  19. //根据启动选项调用对应的方法执行操作
  20. switch (startOpt) {
  21. //格式化当前Namenode, 调用format()方法执行格式化操作
  22. case FORMAT:
  23. aborted = format(conf, startOpt.getForceFormat(),
  24. startOpt.getInteractiveFormat());
  25. terminate(aborted ? 1 : 0);
  26. return null; // avoid javac warning
  27. case GENCLUSTERID:
  28. // 代码略...
  29. //回滚上一次升级, 调用doRollback()方法执行回滚操作。
  30. case ROLLBACK:
  31. // 代码略...
  32. // 拷贝Active Namenode的最新命名空间数据到StandbyNamenode,
  33. // 调用BootstrapStandby.run()方法执行操作
  34. case BOOTSTRAPSTANDBY:
  35. // 代码略...
  36. //初始化editlog的共享存储空间, 并从Active
  37. //Namenode中拷贝足够的editlog数据, 使得Standby节点能够顺利启动。 这里调用
  38. //了静态方法initializeSharedEdits()执行操作
  39. case INITIALIZESHAREDEDITS:
  40. // 代码略...
  41. case BACKUP:
  42. //启动checkpoint节点, 也是直接构造BackupNode对象并返回。
  43. case CHECKPOINT:
  44. // 代码略...
  45. //恢复损坏的元数据以及文件系统, 这里调用了doRecovery()方法执行操作
  46. case RECOVER:
  47. // 代码略...
  48. //确认配置文件夹存在, 并且打印fsimage文件和文件系统的元数据版本
  49. case METADATAVERSION:
  50. // 代码略...
  51. //升级Namenode, 升级完成后关闭Namenode。
  52. case UPGRADEONLY:
  53. // 代码略...
  54. //在默认情况下直接构造NameNode对象并返回
  55. default:
  56. // 初始化 度量服务
  57. DefaultMetricsSystem.initialize("NameNode");
  58. // 构建NameNode
  59. return new NameNode(conf);
  60. }
  61. }

格式化操作format

生成一个集群id,然后构建一个FSImage

clusterId:规则 CID-UUID 如:CID-d5e21420-46b8-41fb-acc7-8562be611472

  1. // 构建 FSImage
  2. FSImage fsImage = new FSImage(conf, nameDirsToFormat, editDirsToFormat);

然后根据 FSImage 构建和配置文件构建 FSNamesystem,最后再调用

  1. fsImage.format(fsn, clusterId, force);

这个进行初始化操作,生成文件

  1. /**
  2. * Verify that configured directories exist, then
  3. * Interactively confirm that formatting is desired
  4. * for each existing directory and format them.
  5. *
  6. * @param conf configuration to use
  7. * @param force if true, format regardless of whether dirs exist
  8. * @return true if formatting was aborted, false otherwise
  9. * @throws IOException
  10. */
  11. private static boolean format(Configuration conf, boolean force,
  12. boolean isInteractive) throws IOException {
  13. // nsId = null
  14. String nsId = DFSUtil.getNamenodeNameServiceId(conf);
  15. // namenodeId = null
  16. String namenodeId = HAUtil.getNameNodeId(conf, nsId);
  17. initializeGenericKeys(conf, nsId, namenodeId);
  18. //读取配置dfs.namenode.support.allow.format namenode是否可以格式化: 默认true
  19. checkAllowFormat(conf);
  20. if (UserGroupInformation.isSecurityEnabled()) {
  21. InetSocketAddress socAddr = DFSUtilClient.getNNAddress(conf);
  22. SecurityUtil.login(conf, DFS_NAMENODE_KEYTAB_FILE_KEY,
  23. DFS_NAMENODE_KERBEROS_PRINCIPAL_KEY, socAddr.getHostName());
  24. }
  25. Collection<URI> nameDirsToFormat = FSNamesystem.getNamespaceDirs(conf);
  26. // list<URI> 0 : file:/tools/hadoop-3.2.1/data/namenode
  27. List<URI> sharedDirs = FSNamesystem.getSharedEditsDirs(conf);
  28. List<URI> dirsToPrompt = new ArrayList<URI>();
  29. dirsToPrompt.addAll(nameDirsToFormat);
  30. dirsToPrompt.addAll(sharedDirs);
  31. // list<URI> 0 : file:/tools/hadoop-3.2.1/data/namenode
  32. List<URI> editDirsToFormat = FSNamesystem.getNamespaceEditsDirs(conf);
  33. // if clusterID is not provided - see if you can find the current one
  34. String clusterId = StartupOption.FORMAT.getClusterId();
  35. if(clusterId == null || clusterId.equals("")) {
  36. // Generate a new cluster id
  37. // 生成新的id 举例: CID-UUID ==> CID-d5e21420-46b8-41fb-acc7-8562be611472
  38. clusterId = NNStorage.newClusterID();
  39. }
  40. System.out.println("Formatting using clusterid: " + clusterId);
  41. // 构建 FSImage
  42. FSImage fsImage = new FSImage(conf, nameDirsToFormat, editDirsToFormat);
  43. //构建 FSNamesystem
  44. try {
  45. FSNamesystem fsn = new FSNamesystem(conf, fsImage);
  46. fsImage.getEditLog().initJournalsForWrite();
  47. // Abort NameNode format if reformat is disabled and if
  48. // meta-dir already exists
  49. if (conf.getBoolean(DFSConfigKeys.DFS_REFORMAT_DISABLED,
  50. DFSConfigKeys.DFS_REFORMAT_DISABLED_DEFAULT)) {
  51. force = false;
  52. isInteractive = false;
  53. for (StorageDirectory sd : fsImage.storage.dirIterable(null)) {
  54. if (sd.hasSomeData()) {
  55. throw new NameNodeFormatException(
  56. "NameNode format aborted as reformat is disabled for "
  57. + "this cluster.");
  58. }
  59. }
  60. }
  61. if (!fsImage.confirmFormat(force, isInteractive)) {
  62. return true; // aborted
  63. }
  64. fsImage.format(fsn, clusterId, force);
  65. } catch (IOException ioe) {
  66. LOG.warn("Encountered exception during format: ", ioe);
  67. fsImage.close();
  68. throw ioe;
  69. }
  70. return false;
  71. }

格式化后生成的文件和内容

格式化之后,会在配置 namenode 的目录生成一个 current 文件夹,里面会有四个文件

VERSION 记录版本信息 fsimage_0000000000000000000.md5 记录fsimage_0000000000000000000对应的md5 fsimage_0000000000000000000 namenode对应的fsimage文件 seen_txid 事务标识

  1. -rw-r--r--@ 1 sysadmin staff 218 9 6 22:11 VERSION
  2. -rw-r--r--@ 1 sysadmin staff 401 9 6 22:13 fsimage_0000000000000000000
  3. -rw-r--r-- 1 sysadmin staff 62 9 6 22:13 fsimage_0000000000000000000.md5
  4. -rw-r--r--@ 1 sysadmin staff 2 9 6 22:11 seen_txid
  • Version 文件

    1. #Sun Sep 06 22:11:02 CST 2020
    2. namespaceID=1147175932
    3. clusterID=CID-d5e21420-46b8-41fb-acc7-8562be611472
    4. cTime=1599401184918
    5. storageType=NAME_NODE
    6. blockpoolID=BP-1598486647-192.168.8.156-1599401184918
    7. layoutVersion=-65
  • Seen_txid: 存储一个数字0

  • fsimage_0000000000000000000.md5 记录 fsimage_0000000000000000000 对应的 md5 值

  • fsimage_0000000000000000000:这里面的内容比较繁琐主要包含以下信息:

    • 保存命名空间信息
    • 保存 ErasureCoding 信息
    • 保存命名空间中的inode信息
    • 保存快照信息
    • 保存安全信息
    • 保存缓存信息
    • 保存StringTable