2021-11-03 hadoop NameNode启动

ps: 本文参考hadoop-3.3.0

1 NameNode简介

namenode是hadoop架构中最重要的角色之一,NameNode主要管理管理着名称空间表(文件名与block的映射,存储在磁盘上且十分重要)和inodes表(块与机器的映射,只要namenode出现就会存在),通常情况下一个集群中有且仅有一个活跃的NameNode,从Hadoop2开始,为了保证集群中的高可用,可以配置两个namenode,一个active另一个为standby。但这种情况下,当集群规模达到一定程度时,namenode仍然会成为集群的瓶颈,因此在这种情况下,hadoop Federation应运而生,即一个集群中允许多组NameNode提供服务,每组namenode各自分管一部分目录(NameSpace,BlockPool),彼此之间相互隔离,但共享相同的DataNode。

但Federation模式虽然间接的扩展了NameNode,但是由于Federation中schema需要使用ViewFs且无法兼容HDFS,因此这种情况下需要对运行在集群上的schema做一个替换。因此Hadoop3中新增的的多个NameNode的特性就显得十分有效,理论上支持使用>2个NameNode,但官方建议使用3-5个NameNode节点。

还有一点内容需要了解,从hadoop3.3.0开始,社区新增了一个新feature,即新增一个observe namenode,为了解决active 访问负载过高的问题,将部分read request 转移到observer上可以大幅的降低active namenode的负载。

2 NameNode的启动

2.1 main()方法

首先进入NameNode的main方法,通过解析各个参数执行NameNode的启动:

public static void main(String argv[]) throws Exception {
    // 这里会做一个判断是否是help操作
    if (DFSUtil.parseHelpArgument(argv, NameNode.USAGE, System.out, true)) {
      System.exit(0);
    }

    try {
      StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);
      // 创建NameNode
      NameNode namenode = createNameNode(argv, null);
      // 进程持续运行,运行NameNodeRpcServer#join,从而实现rpc的通信
      if (namenode != null) {
        namenode.join();
      }
    } catch (Throwable e) {
      LOG.error("Failed to start namenode.", e);
      terminate(1, e);
    }
  }

2.2 createNameNode

此方法主要是用来创建一个NameNode,根据传进去的参数创建不同的NameNode,当没有特别的参数指定时,创建一个默认的NameNode,即正常的NameNode

public static NameNode createNameNode(String argv[], Configuration conf)
      throws IOException {
    LOG.info("createNameNode " + Arrays.asList(argv));
    if (conf == null)
      conf = new HdfsConfiguration();
    // Parse out some generic args into Configuration.
    GenericOptionsParser hParser = new GenericOptionsParser(conf, argv);
    argv = hParser.getRemainingArgs();
    // Parse the rest, NN specific args.
    /**
     *  获取启动方式:默认是regular
     *    1. format:格式化,验证配置的目录(namenode dir,edit dir, share edit dir)
     			是否存在并且格式化是否是预期的
     *    2. gen clusterId:生成新的cluster id
     *    3. regular:正常启动namenode
     *    4. backup:
     *    5. checkpoint:和backup一样都是创建一个BackupNode,主要包括checkpoint和
     			backup的NameNode#role,用来周期性的将edit落盘,
                backup还有一点不同的是会同步active的namespace信息
     *    6. observer:3.3.0新增的一个namenode类型,可以减轻active namenode的读负载
     *    7. upgrade或者upgrade only:升级操作
     *    8. rollingUpgrade:滚动升级
     *    9. rollback:从升级系统中回滚到前一个版本,这个参数必须在停止的集群分布式文件系统中使用
     *    10. import:从指定的一个checkpoint中恢复集群
     *    11. bootstrap standby:启动standby node,仅在初次HA的时候从active 复制namespace数据
     *    12. initialize sharded edits:初始化share edit目录,便于standby nn启动
     *    13. recover:单个元数据存储时可以通过此命令恢复namenode
     *    14. metadata version:元数据信息
     *    15. null:参数不正确
     */
    StartupOption startOpt = parseArguments(argv);
    if (startOpt == null) {
      printUsage(System.err);
      return null;
    }
    // 将startOpt添加到配置中,用户后面创建不同的NameNode
    setStartupOption(conf, startOpt);

    boolean aborted = false;
    // 根据配置创建不同的NameNode
    switch (startOpt) {
    case FORMAT:
      aborted = format(conf, startOpt.getForceFormat(),
          startOpt.getInteractiveFormat());
      terminate(aborted ? 1 : 0);
      return null; // avoid javac warning
    case GENCLUSTERID:
      String clusterID = NNStorage.newClusterID();
      LOG.info("Generated new cluster id: {}", clusterID);
      terminate(0);
      return null;
    case ROLLBACK:
      aborted = doRollback(conf, true);
      terminate(aborted ? 1 : 0);
      return null; // avoid warning
    case BOOTSTRAPSTANDBY:
      String[] toolArgs = Arrays.copyOfRange(argv, 1, argv.length);
      int rc = BootstrapStandby.run(toolArgs, conf);
      terminate(rc);
      return null; // avoid warning
    case INITIALIZESHAREDEDITS:
      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;
    default:
      DefaultMetricsSystem.initialize("NameNode");
      return new NameNode(conf);
    }
  }

这个方法主要是根据参数创建各种不同类型的NameNode,默认是Regular,创建普通的NameNode。另外还有其他几种方式:

  • format:格式化NameNode,建立NameNode节点的文件结构。带有format参数启动NameNode节点时,首先启动NameNode节点,然后对其机型格式化,再关闭节点,如果文件目录已经存在当前文件系统,则会提示用户。它有两个参数nonInteractive和force,nonInteractive表示如果NameNode节点的文件夹在当前的底层文件系统中存在,那么用户将不会收到提示,并且当前的格式化会失败,force表示不管NameNode的目录存不存在,强制格式化NameNode节点,也不会提示用户,如果nonInteractive和force参数同时存在,那么force参数将会被忽略
  • 其他几种方式参见注释

2.3 NameNode构造

protected NameNode(Configuration conf, NamenodeRole role)
      throws IOException {
    super(conf);
    this.tracer = new Tracer.Builder("NameNode").
        conf(TraceUtils.wrapHadoopConf(NAMENODE_HTRACE_PREFIX, conf)).
        build();
    this.tracerConfigurationManager =
        new TracerConfigurationManager(NAMENODE_HTRACE_PREFIX, conf);
    this.role = role;
    String nsId = getNameServiceId(conf);
    String namenodeId = HAUtil.getNameNodeId(conf, nsId);
    clientNamenodeAddress = NameNodeUtils.getClientNamenodeAddress(
        conf, nsId);

    if (clientNamenodeAddress != null) {
      LOG.info("Clients should use {} to access"
          + " this namenode/service.", clientNamenodeAddress);
    }
    // 是否启用HA
    this.haEnabled = HAUtil.isHAEnabled(conf, nsId);
    // 获取当前是Active或者Standby状态
    state = createHAState(getStartupOption(conf));
    this.allowStaleStandbyReads = HAUtil.shouldAllowStandbyReads(conf);
    this.haContext = createHAContext();
    try {
      initializeGenericKeys(conf, nsId, namenodeId);
      // NameNode初始化
      initialize(getConf());
      state.prepareToEnterState(haContext);
      try {
        haContext.writeLock();
        // 启动对应状态的服务,如active、standby
        state.enterState(haContext);
      } finally {
        haContext.writeUnlock();
      }
    } catch (IOException e) {
      this.stopAtException(e);
      throw e;
    } catch (HadoopIllegalArgumentException e) {
      this.stopAtException(e);
      throw e;
    }
    notBecomeActiveInSafemode = conf.getBoolean(
        DFS_HA_NN_NOT_BECOME_ACTIVE_IN_SAFEMODE,
        DFS_HA_NN_NOT_BECOME_ACTIVE_IN_SAFEMODE_DEFAULT);
    this.started.set(true);
  }

在初始化中会进行一下参数的补充,检查当前NameNode的状态,是否HA之类的,而后根据给定状态启动对应服务。最重要的是initialize(conf),用户namenode完成初始化操作。

2.4 initialize

protected void initialize(Configuration conf) throws IOException {
    if (conf.get(HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS) == null) {
      String intervals = conf.get(DFS_METRICS_PERCENTILES_INTERVALS_KEY);
      if (intervals != null) {
        conf.set(HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS,
          intervals);
      }
    }

    // 启动一些监控服务与配置添加
    UserGroupInformation.setConfiguration(conf);
    loginAsNameNodeUser(conf);

    NameNode.initMetrics(conf, this.getRole());
    StartupProgressMetrics.register(startupProgress);

    pauseMonitor = new JvmPauseMonitor();
    pauseMonitor.init(conf);
    pauseMonitor.start();
    metrics.getJvmMetrics().setPauseMonitor(pauseMonitor);

    if (conf.getBoolean(DFS_NAMENODE_GC_TIME_MONITOR_ENABLE,
        DFS_NAMENODE_GC_TIME_MONITOR_ENABLE_DEFAULT)) {
      long observationWindow = conf.getTimeDuration(
          DFS_NAMENODE_GC_TIME_MONITOR_OBSERVATION_WINDOW_MS,
          DFS_NAMENODE_GC_TIME_MONITOR_OBSERVATION_WINDOW_MS_DEFAULT,
          TimeUnit.MILLISECONDS);
      long sleepInterval = conf.getTimeDuration(
          DFS_NAMENODE_GC_TIME_MONITOR_SLEEP_INTERVAL_MS,
          DFS_NAMENODE_GC_TIME_MONITOR_SLEEP_INTERVAL_MS_DEFAULT,
          TimeUnit.MILLISECONDS);
      gcTimeMonitor = new Builder().observationWindowMs(observationWindow)
          .sleepIntervalMs(sleepInterval).build();
      gcTimeMonitor.start();
      metrics.getJvmMetrics().setGcTimeMonitor(gcTimeMonitor);
    }

    // 启动一个NameNodeHttpServer, 启动0.0.0.0:9870
    if (NamenodeRole.NAMENODE == role) {
      startHttpServer(conf);
    }

    // 根据配置中指定位置的edit和fsImage初始化FsNameSystem,加载fsImage
    loadNamesystem(conf);
    startAliasMapServerIfNecessary(conf);

    // hadoop rpc
    rpcServer = createRpcServer(conf);

    initReconfigurableBackoffKey();

    if (clientNamenodeAddress == null) {
      // This is expected for MiniDFSCluster. Set it now using 
      // the RPC server's bind address.
      clientNamenodeAddress = 
          NetUtils.getHostPortString(getNameNodeAddress());
      LOG.info("Clients are to use " + clientNamenodeAddress + " to access"
          + " this namenode/service.");
    }
    if (NamenodeRole.NAMENODE == role) {
      httpServer.setNameNodeAddress(getNameNodeAddress());
      httpServer.setFSImage(getFSImage());
      if (levelDBAliasMapServer != null) {
        httpServer.setAliasMap(levelDBAliasMapServer.getAliasMap());
      }
    }

    // 启用通用服务,在此之中会检查是否进入blockManager的安全模式
    startCommonServices(conf);
    startMetricsLogger(conf);
  }

3 其他

在此主要介绍NameNode类中中比较重要的几个变量:

/**
* FSNamesystem 是瞬态和持久命名空间状态的容器,并在 NameNode 上完成所有记录工作。
* 主要作用如下:
* 		1. 是 BlockManager、DatanodeManager、DelegationTokens、LeaseManager 等服务的容器。
* 		2. 委托处理修改或检查命名空间的 RPC 调用
* 		3. 任何只涉及块的东西(例如块报告),它都委托给 BlockManager
* 		4. 任何只涉及文件信息(例如权限、mkdirs)的东西,它都会委托给 FSDirectory
* 		5. 任何跨越上述两个组件的东西都应该在这里协调。
* 		6. 记录变动到FsEditLog
*
* 此变量保存的内容:
* 		1. 有效文件名和blockList的映射;
* 		2. 合法的block集
* 		3. block和机器列表的映射
* 		4. 机器和blockList的映射
* 		5. 更新心跳机器的 LRU 缓存
*/
protected FSNamesystem namesystem; 
// namenode的角色分类,包括NameNode backup checkpoint
protected final NamenodeRole role;
// Ha状态,包括active standby backup三种实现
private volatile HAState state;

// 负责namenode的http调用
protected NameNodeHttpServer httpServer;

// 主要负责处理namenode的rpc调用
private NameNodeRpcServer rpcServer;

下一篇:安全模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值