hadoop2.7.0源码之NameNode启动流程

本文将结合hadoop2.7.0版本的源码与UML图对NameNode的启动流程进行深入剖析,旨在更深入地理解NameNode启动的整体逻辑

第一、二步:找到NameNode的启动入口main()方法,进入方法体createNameNode()

public static void main(String argv[]) throws Exception {
  if (DFSUtil.parseHelpArgument(argv, NameNode.USAGE, System.out, true)) {
    System.exit(0);
  }
  try {
    StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);
    //TODO:创建NameNode对象
    NameNode namenode = createNameNode(argv, null);
    if (namenode != null) {
      namenode.join();
    }
  } catch (Throwable e) {
    LOG.error("Failed to start namenode.", e);
    terminate(1, e);
  }
}

第三步:在createNameNode()方法中会对传入的参数进行解析,并判断事件类型,由于本文研究的是启动流程,故可直接看default分支

default: {
  DefaultMetricsSystem.initialize("NameNode");
  /*TODO:以上都不是的,就是实例化NameNode*/
  return new NameNode(conf);
}

第四步:在NameNode的构造方法中调用了initialize初始化方法

protected NameNode(Configuration conf, NamenodeRole role) 
    throws IOException { 
  ...
  try {
    initializeGenericKeys(conf, nsId, namenodeId);
    //TODO: 初始化方法
    initialize(conf);
   ...
}

第五步:调用startHttpServer(),启动HttpServer

第六步:进入startHttpServer()方法,并实例化NameNodeHttpServer类

private void startHttpServer(final Configuration conf) throws IOException {
  //TODO: 设置httpServer的ip、端口
  httpServer = new NameNodeHttpServer(conf, this, getHttpServerBindAddress(conf));
  httpServer.start();
  httpServer.setStartupProgress(startupProgress);
}

第七步:调用NameNodeHttpServer类的start()方法

第八、九步:在start()方法内创建了HttpServer2,并随后启动了httpServer,即hadoop的50070端口监控页面

void start() throws IOException {
  ...
  //TODO:启动服务端
  HttpServer2.Builder builder = DFSUtil.httpServerTemplateForNNAndJN(conf,
      httpAddr, httpsAddr, "hdfs",
      DFSConfigKeys.DFS_NAMENODE_KERBEROS_INTERNAL_SPNEGO_PRINCIPAL_KEY,
      DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY);
  httpServer = builder.build();
  if (policy.isHttpsEnabled()) {
    // assume same ssl port for all datanodes
    InetSocketAddress datanodeSslPort = NetUtils.createSocketAddr(conf.getTrimmed(
        DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_KEY, infoHost + ":"
            + DFSConfigKeys.DFS_DATANODE_HTTPS_DEFAULT_PORT));
    httpServer.setAttribute(DFSConfigKeys.DFS_DATANODE_HTTPS_PORT_KEY,
        datanodeSslPort.getPort());
  }
  initWebHdfs(conf);
  httpServer.setAttribute(NAMENODE_ATTRIBUTE_KEY, nn);
  httpServer.setAttribute(JspHelper.CURRENT_CONF, conf);
  //  TODO:往服务端绑定了servlet
  setupServlets(httpServer, conf);
  //  TODO:启动HttpServer服务,对外开放50070端口
  httpServer.start();
  ...
}

第十步:回到NameNode的initialize()方法,继续调用createRpcServer()创建Hadoop RPC

第十一步:实例化NameNodeRpcServer类

protected NameNodeRpcServer createRpcServer(Configuration conf)
    throws IOException {
  return new NameNodeRpcServer(conf, this);
}

第十二步:在NameNodeRpcServer的构造方法中创建serviceRpcServer,主要用于维护NameNode节点与DataNode节点之间的RPC调用,如心跳机制等

public NameNodeRpcServer(Configuration conf, NameNode nn)
    throws IOException {
...
//TODO:启动ServiceRpcServer
this.serviceRpcServer = new RPC.Builder(conf)
    .setProtocol(
        org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB.class)
    .setInstance(clientNNPbService)
    .setBindAddress(bindHost)
    .setPort(serviceRpcAddr.getPort()).setNumHandlers(serviceHandlerCount)
    .setVerbose(false)
    .setSecretManager(namesystem.getDelegationTokenSecretManager())
    .build();
...
}

第十三步:在NameNodeRpcServer的构造方法中创建clientRpcServer,主要用于客户端与集群之间的操作

public NameNodeRpcServer(Configuration conf, NameNode nn)
    throws IOException {
...
//TODO: 启动clientRpcServer
this.clientRpcServer = new RPC.Builder(conf)
    .setProtocol(
        org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB.class)
    .setInstance(clientNNPbService).setBindAddress(bindHost)
    .setPort(rpcAddr.getPort()).setNumHandlers(handlerCount)
    .setVerbose(false)
    .setSecretManager(namesystem.getDelegationTokenSecretManager()).build();
...
}

第十四步:再次回到NameNode的initialize()方法,调用startCommonServices(),该方法主要的功能有两点:1、检查是否有足够的磁盘空间存储元数据;2、进入安全模式检查,检查是否可以退出安全模式

//    TODO 启动一些公共服务,NameNode PRC的服务就是在里面启动的
//    1)进行资源检查,检查是否有足够磁盘存储元数据
//    2)进入安全模式检查,检查是否可以退出安全模式
    startCommonServices(conf);

第十五步:调用FSNameSystem的startCommonServices()方法

private void startCommonServices(Configuration conf) throws IOException {
  //TODO:FSNameSystem是管理HDFS的元数据
  namesystem.startCommonServices(conf, haContext);
  registerNNSMXBean();
  if (NamenodeRole.NAMENODE != role) {
    startHttpServer(conf);
    httpServer.setNameNodeAddress(getNameNodeAddress());
    httpServer.setFSImage(getFSImage());
  }
  //启动RPC
  rpcServer.start();
  ...
}

第十六步:进入FSNameSystem的startCommonServices()方法,首先执行了checkAvailableResources()方法,检查是否有足够的磁盘空间存储元数据信息

  void startCommonServices(Configuration conf, HAContext haContext) throws IOException {
    this.registerMBean(); // register the MBean for the FSNamesystemState
    writeLock();
    this.haContext = haContext;
    try {
//      需要检查三个目录,因为三个目录都涉及到了元数据
//      NameNode的两个目录:存储fsimage的目录、存储editlog的目录
//      JournalNode也存储有元数据,HA
      nnResourceChecker = new NameNodeResourceChecker(conf);
//      TODO:检查是否有足够的磁盘存储元数据
      checkAvailableResources();
      assert safeMode != null && !isPopulatingReplQueues();
      StartupProgress prog = NameNode.getStartupProgress();
      prog.beginPhase(Phase.SAFEMODE);
      prog.setTotal(Phase.SAFEMODE, STEP_AWAITING_REPORTED_BLOCKS,
        getCompleteBlocksTotal());
//      TODO:HDFS的安全模式的检查(比较重要)
      setBlockTotal();
//      TODO:启动重要服务
      blockManager.activate(conf);
    } finally {
      writeUnlock();
    }
    
    ...
  }
  void checkAvailableResources() {
    Preconditions.checkState(nnResourceChecker != null,
        "nnResourceChecker not initialized");
//    TODO:返回资源是否充足
    hasResourcesAvailable = nnResourceChecker.hasAvailableDiskSpace();
  }

第十七步:调用setBlockTotal()进行安全模式的检查

  public void setBlockTotal() {
    // safeMode is volatile, and may be set to null at any time
    SafeModeInfo safeMode = this.safeMode;
    if (safeMode == null)
      return;
//    TODO:设置安全模式
//    getCompleteBlocksTotal()获取集群正常的block个数
    safeMode.setBlockTotal((int)getCompleteBlocksTotal());
  }

第十八步:调用SafeModeInfo实例的setBlockTotal()设置安全模式

    private synchronized void setBlockTotal(int total) {
      this.blockTotal = total;
//      TODO:计算阈值
//      举例:1000 * 0.999 = 999 threshold默认是0.999
      this.blockThreshold = (int) (blockTotal * threshold);
      this.blockReplQueueThreshold = 
        (int) (blockTotal * replQueueThreshold);
      if (haEnabled) {
        // After we initialize the block count, any further namespace
        // modifications done while in safe mode need to keep track
        // of the number of total blocks in the system.
        this.shouldIncrementallyTrackBlocks = true;
      }
      if(blockSafe < 0)
        this.blockSafe = 0;
//      TODO:检查安全模式
      checkMode();
    }

第十九步:调用checkMode()检查安全模式

   private void checkMode() {
      ...
      // if smmthread is already running, the block threshold must have been 
      // reached before, there is no need to enter the safe mode again
//      TODO:判断是否进入安全模式
      if (smmthread == null && needEnter()) {
//        TODO:进入安全模式
        enter();
        // check if we are ready to initialize replication queues
        if (canInitializeReplQueues() && !isPopulatingReplQueues()
            && !haEnabled) {
          initializeReplQueues();
        }
        reportStatus("STATE* Safe mode ON.", false);
        return;
      }
      ...
    }

第二十步:调用needEnter()方法判断是需要进入安全模式

//    TODO:是否需要进入安全模式
    private boolean needEnter() {
//      1)处于安全的block个数是否小于阈值
      return (threshold != 0 && blockSafe < blockThreshold) ||
//              2)存活的dataNode个数是否小于阈值
        (datanodeThreshold != 0 && getNumLiveDataNodes() < datanodeThreshold) ||
//              3)NameNode存储元数据的是否有足够的磁盘空间,默认剩余空间需要大于100M
        (!nameNodeHasResourcesAvailable());
    }

第二十一步:回到FSNamesystem类的startCommonServices方法中,调用BlockManager的activate()方法启动服务

void startCommonServices(Configuration conf, HAContext haContext) throws IOException {
...
    //TODO:启动重要服务
    blockManager.activate(conf);
...
}

第二十二步:回到NameNode的startCommonServices()方法,调用NameNodeRpcServer的start(),完成NameNode的启动

  private void startCommonServices(Configuration conf) throws IOException {
//    TODO FSNameSystem是管理HDFS的元数据
    namesystem.startCommonServices(conf, haContext);
    registerNNSMXBean();
    if (NamenodeRole.NAMENODE != role) {
      startHttpServer(conf);
      httpServer.setNameNodeAddress(getNameNodeAddress());
      httpServer.setFSImage(getFSImage());
    }
//    启动RPC
    rpcServer.start();
   ...
  }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值