003-hadoop二次开发-NameNode启动流程

  • main函数


main函数

一旦调用org.apache.hadoop.hdfs.server.namenode.NameNode,那么在该类下一定有个main函数。
启动NameNode需要提交参数,首先对参数要进行合法校验,

if (DFSUtil.parseHelpArgument(argv, NameNode.USAGE, System.out, true)) {
  System.out.println("####参数校验#####");
  System.exit(0);
  //hadooop namenode -format
  //hadoop -datemon.sh start namenode
  //hadoop-datemon.sh start nn
}

然后启动打印日志,

try {
  //启动打印日志
  StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);
  //TODO 创建NameNode核心代码
  NameNode namenode = createNameNode(argv, null);
  if (namenode != null) {
    //就是线程的join
    //会使主线程进入等待池并等待t线程执行完毕后才会被唤醒
    namenode.join();
  }
} catch (Throwable e) {
  LOG.error("Failed to start namenode.", e);
  terminate(1, e);
}

通过createNameNode(argv, null)进入,

if (conf == null)
  conf = new HdfsConfiguration();
StartupOption startOpt = parseArguments(argv);

首先判断配置文件是否为空,如果为空创建一个配置文件,然后去解析传进的配置参数,传进去是一些参数字符串,要解析成枚举等变量,在parseArguments(argv)中进行解析,返回StartupOption 。(ctrl+alt+左箭头返回上一步操作处)

if (startOpt == null) {
  printUsage(System.err);
  return null;
}

如果没有解析StartupOption打印异常,

//将启动参数加入到配置中
setStartupOption(conf, startOpt);
private static void setStartupOption(Configuration conf, StartupOption opt) {
    conf.set(DFS_NAMENODE_STARTUP_KEY, opt.name());
  }

如果解析出StartupOption,将参数加入配置中,设置初始化参数。
接下来switch模式匹配进行模式判断,

/**
 * 我们在集群中操作 , 比如 :
 * hdfs namenode -format
 * hadoop fs -put XXX /
 * 正常情况下,是先启动namenode
 * hadoop-daemon.sh start namenode
 * */
switch (startOpt) {
  //格式化
  case FORMAT: {//hadoop namenode -format
    boolean aborted = format(conf, startOpt.getForceFormat(),
            startOpt.getInteractiveFormat());
    terminate(aborted ? 1 : 0);
    return null; // avoid javac warning
  }
  // 产生集群ID.
  case GENCLUSTERID: {
    System.err.println("Generating new cluster id:");
    System.out.println(NNStorage.newClusterID());
    terminate(0);
    return null;
  }
  //定版本
  case FINALIZE: {
    System.err.println("Use of the argument '" + StartupOption.FINALIZE +
            "' is no longer supported. To finalize an upgrade, start the NN " +
            " and then run `hdfs dfsadmin -finalizeUpgrade'");
    terminate(1);
    return null; // avoid javac warning
  }
  //回滚
  case ROLLBACK: {
    boolean aborted = doRollback(conf, true);
    terminate(aborted ? 1 : 0);
    return null; // avoid warning
  }
  //同步active节点的快照(配置完HA需要运行这个命令同步active节点的快照(配置完HA需要运行这个命令
  case BOOTSTRAPSTANDBY: {
    String toolArgs[] = Arrays.copyOfRange(argv, 1, argv.length);
    int rc = BootstrapStandby.run(toolArgs, conf);
    terminate(rc);
    return null; // avoid warning
  }
  //向备用节点共享一组edits日志
  case INITIALIZESHAREDEDITS: {
    boolean aborted = initializeSharedEdits(conf,
            startOpt.getForceFormat(),
            startOpt.getInteractiveFormat());
    terminate(aborted ? 1 : 0);
    return null; // avoid warning
  }
  //启动冷备或者温备名字节点
  case BACKUP:
  case CHECKPOINT: {
    NamenodeRole role = startOpt.toNodeRole();
    DefaultMetricsSystem.initialize(role.toString().replace(" ", ""));
    return new BackupNode(conf, role);
  }
  //恢复损坏的元数据以及文件系统
  case RECOVER: {
    NameNode.doRecovery(startOpt, conf);
    return null;
  }
  //检查配置的正确性
  case METADATAVERSION: {
    printMetadataVersion(conf);
    terminate(0);
    return null; // avoid javac warning
  }
  //以升级的方式启动
  case UPGRADEONLY: {
    DefaultMetricsSystem.initialize("NameNode");
    new NameNode(conf);
    terminate(0);
    return null;
  }
  //正常启动NameNode
  default: {
    //初始化metric系统(所有大数据框都依赖于metric)
    //通过metric监控启动时长,某个任务消耗时间等
    DefaultMetricsSystem.initialize("NameNode");
    //TODO
    return new NameNode(conf);
  }
}

通过new NameNode(conf)走到

public NameNode(Configuration conf) throws IOException {
    this(conf, NamenodeRole.NAMENODE);
  }

通过this走到namenode核心领域,

/**
   * 1、对namenode做参数的注册(fs.defaultFS、rpc地址等)
   * 2、初始化
   * 3、根据初始化处理的结果,namenode进入对应的状态(active、backup、standby)
   * */
  protected NameNode(Configuration conf, NamenodeRole role) 
      throws IOException { 
    this.conf = conf;
    this.role = role;//保存NameNode的角色信息
    //设置clients访问nomenode或nameservice的访问地址  配置项fs.defaultFS:hadoop01:9000
    setClientNamenodeAddress(conf);
    String nsId = getNameServiceId(conf);
    String namenodeId = HAUtil.getNameNodeId(conf, nsId);
    //ha相关
    this.haEnabled = HAUtil.isHAEnabled(conf, nsId);
    //根据用户设置的启动参数,确定启动以后的初始状态,如果是正常启动,则全部直接进入Standby状态
    state = createHAState(getStartupOption(conf));
    this.allowStaleStandbyReads = HAUtil.shouldAllowStandbyReads(conf);
    //TODO 在创建HA的时候,也启动了standByNameNode的服务
    this.haContext = createHAContext();



    try {
      //给联邦模式下准备的,主要是设置联邦模式下namenode的地址和RPC地址
      initializeGenericKeys(conf, nsId, namenodeId);
      //TODO
      initialize(conf);
      // HA相关
      try {
        haContext.writeLock();
        state.prepareToEnterState(haContext);
        state.enterState(haContext);
      } finally {
        haContext.writeUnlock();
      }
    } catch (IOException e) {
      this.stop();
      throw e;
    } catch (HadoopIllegalArgumentException e) {
      this.stop();
      throw e;
    }
    this.started.set(true);
  }