1. 程式人生 > >Hadoop源碼學習筆記之NameNode啟動流程分析二:http server啟動源碼剖析

Hadoop源碼學習筆記之NameNode啟動流程分析二:http server啟動源碼剖析

選擇 stop down gen java類 屬性 功能 集群 aslist

NameNodeHttpServer啟動源碼剖析,這一部分主要按以下步驟進行:

  一、源碼調用分析

  二、偽代碼調用流程梳理

  三、http server服務流程圖解

第一步,源碼調用分析

  前一篇文章已經鎖定到了NameNode.java類文件,搜索找到main(),可以看到代碼只有寥寥幾行,再篩除掉一些參數校驗以及try-catch邏輯代碼,

  剩下的核心的代碼甚至只有兩行,如下:

 1   public static void main(String argv[]) throws Exception {
 2     if (DFSUtil.parseHelpArgument(argv, NameNode.USAGE, System.out, true
)) { 3 System.exit(0); 4 } 5 6 try { 7 StringUtils.startupShutdownMessage(NameNode.class, argv, LOG); 8 NameNode namenode = createNameNode(argv, null); 9 if (namenode != null) { 10 namenode.join(); 11 } 12 } catch (Throwable e) { 13 LOG.fatal("Failed to start namenode.", e);
14 terminate(1, e); 15 } 16 }

  根據main()裏面的代碼,可以得知主流程是:1.創建NameNode對象,2.調用join()方法。然後深入到createNameNode()方法中,如下:

public static NameNode createNameNode(String argv[], Configuration conf)
    throws IOException {
  LOG.info("createNameNode " + Arrays.asList(argv));
  if (conf == null)
    conf 
= new HdfsConfiguration(); ......// Parse the rest, NN specific args. StartupOption startOpt = parseArguments(argv); if (startOpt == null) { printUsage(System.err); return null; } // 重要代碼:設置解析出來的參數。 setStartupOption(conf, startOpt); switch (startOpt) { // 如果傳遞的是-format,就是告訴namenode要做目錄結構的格式化,其他的也同理。 case FORMAT: { boolean aborted = format(conf, startOpt.getForceFormat(), startOpt.getInteractiveFormat()); terminate(aborted ? 1 : 0); return null; // avoid javac warning } ...... case ROLLBACK: { boolean aborted = doRollback(conf, true); terminate(aborted ? 1 : 0); return null; // avoid warning } ...... default: { // 核心代碼:如果正常情況下啟動一個NameNode進程,代碼會執行到此處,實例化一個NameNode對象。 DefaultMetricsSystem.initialize("NameNode"); return new NameNode(conf); } } }

  這段代碼看著很長,其實最重要的也就標粗的兩行:

    • setStartupOption(conf, startOpt);

      這行代碼是用來設置解析出來的參數。比如執行一條shell命令:hdfs namenode -format,其中的-format就是作為參數傳遞進來

      進行解析,然後經過下面的switch()進行判斷,進入到格式化的代碼部分。其他的Hadoop命令參數比如-rollback也同理,都會進入到這個switch(),

      根據傳入的參數去執行相應的功能代碼。

    • new NameNode(conf);

      switch()裏前面的選擇都是一些NameNode的功能模塊,比如初始化、回滾、產生集群id等,而正常情況下啟動NameNode進程會進入到default模塊,

      在這裏會實例化一個NameNode對象。

  接下來,就繼續dive into到NameNode的構造方法中:

protected NameNode(Configuration conf, NamenodeRole role) 
    throws IOException {
  // 初始化一些參數及配置
  this.conf = conf;
  this.role = role;
  setClientNamenodeAddress(conf);
  ......
  try {
    initializeGenericKeys(conf, nsId, namenodeId);
    // 核心代碼:初始化NameNode實例對象。
    initialize(conf);
    try {
      ......
  } catch (HadoopIllegalArgumentException e) {
    this.stop();
    throw e;
  }
  this.started.set(true);
}

  可以看到除了上面的一些參數配置之外,最重要的一行代碼就是initialize()方法了。也可以根據NameNode的註釋說明結合集群啟動命令等

  推測到,NameNodeServer的代碼肯定是在這個初始化方法裏面。繼續往下深入,

protected void initialize(Configuration conf) throws IOException {
  // 可以通過找到下面變量名的映射,在hdfs-default.xml中找到對應的配置
  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);
    }
  }

  ......

  // 核心代碼:啟動HttpServer
  if (NamenodeRole.NAMENODE == role) {
    startHttpServer(conf);
  }

  this.spanReceiverHost = SpanReceiverHost.getInstance(conf);

  // 核心代碼:後面FSNamesystem初始化篇研究
  loadNamesystem(conf);

  // 核心代碼:後面rpc server啟動流程篇研究
  rpcServer = createRpcServer(conf);
  
  ......
  
  startCommonServices(conf);
}

  這一段代碼裏面,核心代碼有三行,但是目前只針對http server啟動流程進行挖掘,下面兩行核心代碼會有針對性的去研究。

繼續深入startHttpServer()方法,

private void startHttpServer(final Configuration conf) throws IOException {
    httpServer = new NameNodeHttpServer(conf, this, getHttpServerBindAddress(conf));
    // 核心代碼
    httpServer.start();
    httpServer.setStartupProgress(startupProgress);
  }

  繼續深入httpServer.start()方法,看看裏面都做了哪些事情,

void start() throws IOException {
  HttpConfig.Policy policy = DFSUtil.getHttpPolicy(conf);
  final String infoHost = bindAddress.getHostName();

  // 重要代碼
  final InetSocketAddress httpAddr = bindAddress;
  final String httpsAddrString = conf.get(
      DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY,
      DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_DEFAULT);
  InetSocketAddress httpsAddr = NetUtils.createSocketAddr(httpsAddrString);

  ......

  // 重要代碼
  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();

  ......

  initWebHdfs(conf);

  httpServer.setAttribute(NAMENODE_ATTRIBUTE_KEY, nn);
  httpServer.setAttribute(JspHelper.CURRENT_CONF, conf);

  // 核心代碼:HttpServer2綁定了一堆servlet,定義好了接收哪些http請求,接收到了請求由誰來處理。
  setupServlets(httpServer, conf);
  // 核心代碼:啟動httpServer
  httpServer.start();

  ......
}

  這段代碼中的bindAddress,經過一步一步的溯源,可以對應到hdfs-default.xml中的這段屬性:

<property>
  <name>dfs.namenode.http-address</name>
  <value>0.0.0.0:50070</value>
  <description>
    The address and the base port where the dfs namenode web ui will listen on.
  </description>
</property>

  可以得知bindAddress就是http server綁定在哪個端口上,默認是0.0.0.0:50070,當然也可以自己配置。結合以往的集群啟動經驗,啟動成功後我們一般會

  在瀏覽器訪問http://xx.xx.xx.xx:50070這個hdfs管理工作臺,可以查看集群的狀態、元數據、目錄結構、block信息等。這其實就是NameNode的http server

  提供的服務。

  具體http server是什麽?往下看會有這麽一行代碼:

  HttpServer2.Builder builder = ....;

  httpServer = builder.build();

  這段代碼指示了http server是具體的哪個類——HttpServer2.java。這個類是Hadoop實現的一套http服務,用於接收http請求。並且這一塊是Hadoop抽離出來

  獨立的一個模塊,在hadoop-common包中,是整個Hadoop框架通用的一套http server服務。具體怎麽實現的不在本篇研究範圍,就不做深究了。

  繼續往下看,setupServlets(); 這一行是核心代碼,點進去發現是綁定了各種參數的servlet。這些servlet定義了接收哪些http請求,並且接收到請求之後由哪個

  servlet進行處理。舉個栗子,我們在瀏覽器請求了http://xx.xx.xx.xx:50070/listPath?path=/user/warehouse,此時HttpServer2接收到請求,然後轉發給對應

  的處理listPath功能的servlet,servlet處理完之後再將結果返回。

  最後,就是httpServer.start()了,代碼執行到此處會啟動httpServer,然後客戶端都可以通過http請求進行訪問了。

第二步,偽代碼調用流程梳理。

  現在,對上述的整個流程用偽代碼進行一個梳理,

  1. NameNode.main() 入口函數
  2. createNameNode() 方法通過new NameNode()進行實例化
  3. 實例化的時候,會通過initialize()方法進行初始化操作
  4. 初始化操作主要包含了啟動HttpServer、加載元數據信息、啟動RpcServer這三部分

第三步,http server服務流程圖解。

技術分享圖片

Hadoop源碼學習筆記之NameNode啟動流程分析二:http server啟動源碼剖析