Hadoop源碼分析筆記(十五):名字節點--啓動和停止

名字節點啓動和停止

安全模式

        名字節點啓動時,內存中一片空白,第一要緊的事情就是把命名空間鏡像加載到內存,並應用鏡像編輯日誌,這個過程結束時,會創建一個新的檢查點,包括新命名空間鏡像和一個空的編輯日誌。完成上訴工作後,名字節點開始監聽IPC和HTTP請求,雖然這個時候名字節點已經對外提供服務,但它只是爲客戶端提供了一個只讀視圖,這種名字節點的只讀模式爲安全模式。
       繼續上面的過程,在名字節點第一關係建立起來以後,需要處理數據節點啓動時的若干遠程調用,以建立名字節點第二關係。在安全模式先,需要給名字節點一些時間,使名字節點獲得足夠多的數據節點,數據塊也能獲得足夠多的保存數據塊的數據節點的信息,以高效地運行文件系統。如果名字節點沒有等到足夠多的數據節點,大量的數據塊會初夏副本數不足的情況,進入複製隊列並最終產生複製請求。這在很多情況下顯然是沒有必要的,而且還會浪費系統資源。從前面的代碼分析,我們知道,在安全模式下,名字節點不會向數據節點下發數據塊複製或數據塊刪除指令。
         當收集到足夠多的第二關係信息後,名字節點會離開安全模式,進入正常工作狀態,允許客戶端和系統自身對文件系統進行修改。足夠多的信息指的是文件系統中一定比例的數據塊副本信息達到最低副本水平,默認情況下,需要系統95%的數據塊在第二關係中有一個副本的位置信息。名字節點離開安全模式前,還可以通過配置安全模式等待時間,在滿足上述離開安全模式的副本條件後,讓名字節點繼續等待一段時間,等待時間的默認值爲0,也就是立即離開安全模式。
       安全模式中的配置項:
       dfs.replication.min: 默認值爲 1      對應SafeModeInfo中對應變量:safeReplication      最低副本水平數/系統最小副本數,也用於寫操作中,若completeFileInternal()方法中.
       dfs.safemode.threshold.pct 默認值爲 0.95     對應SafeModeInfo中對應變量:threshold 離開安全模式時系統滿足最低副本水平的副本比列
       dfs.safemode.extension  默認值爲0    對應SafeModeInfo中對應變量:extension  安全模式等待時間,在滿足最小副本水平提交後,離開安全模式的時間,單位是毫秒。
       HDFS安全模式的需求總結如下:
        1)處於安全模式下,HDFS只提供系統的只讀視圖,不能進行修改。
        2)名字節點啓動時,根據配置,檢查第二關係中數據塊的副本信息,滿足條件時離開安全模式。
        3)通過命令行支持安全模式的狀態查詢、等待和設置。

名字節點的啓動

        NameNode.main()是名字節點啓動時的入口,和數據節點一樣,這個main()方法通過類的靜態方法createNameNode()創建了一個NameNode對象,順利創建對象後通過NameNode.join()等待對象執行結束。代碼如下:
       
  /**
   */
  public static void main(String argv[]) throws Exception {
    try {
      StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);
      NameNode namenode = createNameNode(argv, null);
      if (namenode != null)
        namenode.join();
    } catch (Throwable e) {
      LOG.error(StringUtils.stringifyException(e));
      System.exit(-1);
    }
  }
 /**
   * Wait for service to finish.
   * (Normally, it runs forever.)
   */
  public void join() {
    try {
      this.server.join();
    } catch (InterruptedException ie) {
    }
  }
  public static NameNode createNameNode(String argv[], 
                                 Configuration conf) throws IOException {
    if (conf == null)
      conf = new Configuration();
    StartupOption startOpt = parseArguments(argv);
    if (startOpt == null) {
      printUsage();
      return null;
    }
    setStartupOption(conf, startOpt);

    switch (startOpt) {
      case FORMAT:
        boolean aborted = format(conf, true);
        System.exit(aborted ? 1 : 0);
      case FINALIZE:
        aborted = finalize(conf, true);
        System.exit(aborted ? 1 : 0);
      default:
    }
    DefaultMetricsSystem.initialize("NameNode");
    NameNode namenode = new NameNode(conf);
    return namenode;
  }
      createNameNode()方法會處理名字節點的啓動選項,這些選項包括:
      FORMAT:格式化,和數據節點不同,啓動HDFS集羣前需要對名字節點進行格式化,以建立或重新建立名字節點文件結構。
      REGULAR:正常啓動。
      UPGRADE/ROLLBACK/FINALIZE:升級、升級回滾或升級提交
      IMPORT:恢復名字節點上的元數據到某一個檢查點。
      NameNode類的實現中,將大量具體的工作交由FSNamesystem完成,初始化也是一樣的。創建名字節點的FSNamesystem對象時,會完成如下工作:
      1)通過配置項爲FSNamesystem的成員變量賦值。
      2)創建FSDirectory對象,即名字節點第一關係的門面。
      3)通過FSDirectory.loadFSImage(),加載命名空間鏡像並應用編輯日誌。
      4)創建安全模式管理對象SafeModeInfo(),並調用setBlockTotal()更新SafeModeInfo的成員變量blockTotal,調用結束後,名字節點一般應處於安全模式。
      5)創建並啓動心跳檢查線程、租約檢查線程、數據塊複製/刪除線程。
      6)讀入數據節點的include文件和exclude文件,創建並啓動數據節點撤銷檢查線程。
      7)創建網絡佈局感知需要的dnsToSwitchMapping對象,如果可能,分析include文件中包含的主機位置。
      8)讀入配置項${fs.deafult.name}
      FSNamesystem.initialize()讓我們回顧了名字節點的主要組成部分,如名字節點第一關係的FSDirectory、命名空間鏡像和編輯日誌,名字節點第二關係的數據節點管理和數據塊副本複製、刪除,安全模式等。構造玩FSNamesystem對象後,NameNode.initialize()打開IPC服務器,名字節點就開始對外提供服務了。代碼如下:
       
 /**
   * FSNamesystem constructor.
   */
  FSNamesystem(NameNode nn, Configuration conf) throws IOException {
    try {
      initialize(nn, conf);
    } catch(IOException e) {
      LOG.error(getClass().getSimpleName() + " initialization failed.", e);
      close();
      throw e;
    }
  }
/**
   * Initialize FSNamesystem.
   */
  private void initialize(NameNode nn, Configuration conf) throws IOException {
    this.systemStart = now();
    setConfigurationParameters(conf);
    dtSecretManager = createDelegationTokenSecretManager(conf);

    this.nameNodeAddress = nn.getNameNodeAddress();
    this.registerMBean(conf); // register the MBean for the FSNamesystemStutus
    this.dir = new FSDirectory(this, conf);
    StartupOption startOpt = NameNode.getStartupOption(conf);
    this.dir.loadFSImage(getNamespaceDirs(conf),
                         getNamespaceEditsDirs(conf), startOpt);
    long timeTakenToLoadFSImage = now() - systemStart;
    LOG.info("Finished loading FSImage in " + timeTakenToLoadFSImage + " msecs");
    NameNode.getNameNodeMetrics().setFsImageLoadTime(timeTakenToLoadFSImage);
    this.safeMode = new SafeModeInfo(conf);
    setBlockTotal();
    pendingReplications = new PendingReplicationBlocks(
                            conf.getInt("dfs.replication.pending.timeout.sec", 
                                        -1) * 1000L);
    if (isAccessTokenEnabled) {
      accessTokenHandler = new BlockTokenSecretManager(true,
          accessKeyUpdateInterval, accessTokenLifetime);
    }
    this.hbthread = new Daemon(new HeartbeatMonitor());
    this.lmthread = new Daemon(leaseManager.new Monitor());
    this.replmon = new ReplicationMonitor();
    this.replthread = new Daemon(replmon);
    hbthread.start();
    lmthread.start();
    replthread.start();

    this.hostsReader = new HostsFileReader(conf.get("dfs.hosts",""),
                                           conf.get("dfs.hosts.exclude",""));
    this.dnthread = new Daemon(new DecommissionManager(this).new Monitor(
        conf.getInt("dfs.namenode.decommission.interval", 30),
        conf.getInt("dfs.namenode.decommission.nodes.per.interval", 5)));
    dnthread.start();

    this.dnsToSwitchMapping = ReflectionUtils.newInstance(
        conf.getClass("topology.node.switch.mapping.impl", ScriptBasedMapping.class,
            DNSToSwitchMapping.class), conf);
    
    /* If the dns to swith mapping supports cache, resolve network 
     * locations of those hosts in the include list, 
     * and store the mapping in the cache; so future calls to resolve
     * will be fast.
     */
    if (dnsToSwitchMapping instanceof CachedDNSToSwitchMapping) {
      dnsToSwitchMapping.resolve(new ArrayList<String>(hostsReader.getHosts()));
    }
    
    InetSocketAddress socAddr = NameNode.getAddress(conf);
    this.nameNodeHostName = socAddr.getHostName();
    
    registerWith(DefaultMetricsSystem.INSTANCE);
  }

      FSNamesystem和NameNode的構造函數調用各自的initialize()方法失敗時,都有相應的清理函數,釋放已經初始化的資源,具體參考FSNamesystem.close()和NameNode.stop()實現。

名字節點的停止 

         相對於啓動,停止名字節點不需要做太多的工作:名字節點第一關係在執行的過程中會不斷通過日誌將更新持久化,而第二關係則可通過數據節點上保存的數據塊信息回覆。所以,名字節點停止時,只是執行打印的邏輯時,由StringUtils.startupShutdownMessage()方法,通過Runtime.getRuntime().addShutdownHook()添加到關閉掛鉤中,由Java虛擬機在退出時執行。

     版權申明:本文部分摘自【蔡斌、陳湘萍】所著【Hadoop技術內幕 深入解析Hadoop Common和HDFS架構設計與實現原理】一書,僅作爲學習筆記,用於技術交流,其商業版權由原作者保留,推薦大家購買圖書研究,轉載請保留原作者,謝謝!
發佈了1 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章