Hadoop源碼篇 --- NameNode的啓動流程解析

前言

提醒一下,這裏面須要有RPC的基礎,若是對RPC沒有了解的朋友,能夠先跳轉到以往寫的兩篇RPC文章中。java

理論方面:從零開始的高併發(七)--- RPC的介紹,協議及框架node

(可略過)代碼方面:從零開始的高併發(八)--- RPC框架的簡單實現linux

固然也不須要太過深刻,知道點皮毛便可。由於Hadoop中有一個Hadoop RPC須要有點基礎知識。web

暫時先記得下面的知足RPC的條件(非完整):面試

1.不一樣進程間的方法調用瀏覽器

2.RPC分爲服務端和客戶端,客戶端調用服務端的方法,方法的執行是在服務端安全

3.協議說得直白點就是一個接口,可是這個接口必須存在versionID服務器

4.協議裏面會存在抽象方法,這些抽象方法交由服務端實現併發

好的那咱們開始框架

1、NameNode的啓動流程解析

咱們的第一個任務,就是來驗證NameNode是否是一個RPC的服務端

1.1 第一步:找到NameNode的main方法

如今咱們來到Hadoop2.7.0的源碼,先打開NameNode.java的代碼,找到它的main方法

爲了方便你們觀看,我再標好一些註釋

1.2 第二步:點開建立NameNode的createNameNode方法

這個createNameNode方法咱們分紅兩部分看

第一部分:傳參的部分

咱們操做HDFS集羣時,會經過以下一些命令

hdfs namenode -format

hadoop=daemon.sh start namanode
複製代碼

第二部分:switch部分

代碼過長無法截圖,直接複製下來

switch (startOpt) {
  case FORMAT: {
    boolean aborted = format(conf, startOpt.getForceFormat(),
        startOpt.getInteractiveFormat());
    terminate(aborted ? 1 : 0);
    return null; // avoid javac warning
  }
  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
  }
  case BOOTSTRAPSTANDBY: {
    String toolArgs[] = Arrays.copyOfRange(argv, 1, argv.length);
    int rc = BootstrapStandby.run(toolArgs, conf);
    terminate(rc);
    return null; // avoid warning
  }
  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;
  }
  default: {
    DefaultMetricsSystem.initialize("NameNode");
    return new NameNode(conf);
  }
複製代碼

好比第一小段是hdfs namenode -format,它是一個format

天然走的就是這個分支啦。

可是如今我好比輸入的是start 也就是第二小段hadoop=daemon.sh start namanode,由於其餘的都不知足,因此走的就是最後一個分支

再點進去這個new NameNode(conf)

1.3 NameNode的初始化

點進去this方法,其實就是下面的那個

前面的一大截也不須要看,無非就是一些參數的傳遞問題,看這個try裏面的,有一個initialize,這個單詞的中文是初始化,那咱們就再繼續點進去

1.4 初始化的具體步驟

1.4.1 HttpServer

前面的是判斷了一些奇奇怪怪的條件的暫時先無論,直接看到咱們比較敏感的位置

此時咱們看見建立HttpServer的代碼了,咱們若是是初次搭建咱們的大數據集羣時,是否是會訪問一個50070的web頁面,好比

這裏雖然不是啥重要的流程,不過順便解釋一下50070怎麼來的

第二句開始就start了,因此該有的參數第一句確定都有了,咱們看看後面那個getHttpServerBindAddress(conf)

而後咱們看到了這兩個參數,DFS_NAMENODE_HTTP_ADDRESS_KEY 和 DFS_NAMENODE_HTTP_ADDRESS_DEFAULT

DFS_NAMENODE_HTTP_ADDRESS_KEY是本身手動配置的地址,可是通常咱們都沒有去手動配置,因此hadoop會使用一個默認的地址 DFS_NAMENODE_HTTP_ADDRESS_DEFAULT

看到這裏,就知道咱們當時訪問那個網站爲啥是本機ip加一個50070了吧。

1.4.2 對HttpServer2進行加強的servlet

此時咱們回到第一張圖,也就是第二句就是start的那張startHttpServer方法,點進去start方法看看

咱們往下看,做爲一名Java Developer,咱們的關注點天然就是咱們熟悉的servlet了

setupServlets(httpServer, conf);
複製代碼

這裏綁定了一堆的servlet,綁的越多功能越強,咱們再點進去

能夠看到就是瘋狂地add一些各式各樣的servlet,咱們姑且先不看,其實servlet你們應該很是熟悉,點進去先啥都別想直接跳doGet()方法就能看到它們都分別作了什麼了

咱們看到目前爲止能夠先畫一下咱們的流程圖了

1.5 流程圖的第一步

首先咱們如今是有一個HttpServer2(Hadoop本身封裝的HttpServer),它上面綁定了不少提供各類各樣功能的servlet

它對外提供一個50070的端口,瀏覽器發送一個http://ip address:50070/listPaths的請求去請求servlet後,就會返回那個web頁面了

1.6 Hadoop RPC

回到1.4.1中的那張HttpServer的那個位置。在啓動HttpServer後,會加載元數據,可是咱們如今模擬的場景是集羣第一次啓動,第一次啓動的時候是不存在元數據的,因此咱們先直接跳過這個步驟,去到Hadoop RPC的位置

爲何我會知道這個Hadoop RPC會有兩個這樣的服務,這個固然不是我猜的,也並非我找過什麼資料或者點進去看過,而是在NameNode源碼中告訴咱們的

咱們直接把這一段英文放到百度翻譯上看看

這裏它就說明了,NameNode不只僅是個類,仍是個服務器,向外界公開一個IPC Server和一個Http server,補充說明一下,這個IPC Server就是讓開發者去進行命令操做的。這個Http Server就是那個開放了50070界面讓開發者瞭解HDFS的狀況的。而FSNamessystem這個類是管理了HDFS的元數據的

如今咱們知道了,這兩個服務一個是提供給內部的DataNode去調用,而另一個是提供給服務端去調用的

1.6.1 驗證明現協議且協議中存在versionID

這裏的方法起名已經很是直接了,createRpcServer()哈哈哈,咱們點擊進去

這裏的註釋爲寫了建立RPC服務端實現,它return了一個NameNodeRpcServer,那是否是它纔是咱們要找的NameNode服務端呢?

還記得咱們開頭說判斷RPC的依據嗎,3.協議說得直白點就是一個接口,可是這個接口必須存在versionID,好的咱們記住這句話,再點進NameNodeRpcServer

相信這就是咱們想要看到的,它確實實現了一個NamenodeProtocols,感受快要看到真相了是吧,再點進去

我去,繼承了這麼多個協議,怪不得咱們的NameNode的功能如此強大,這裏面得有多少個方法啊,這時候咱們隨便點進去一個接口去看

1.6.2 驗證存在設置參數的過程

知足是一個接口,也有一個versionID了吧,咱們如今以爲它就是服務端了,但是咱們尚未看見那些set服務器地址啊,端口啊···等等這些參數的代碼,因此仍是不能一口咬定,因此咱們如今再退回去到class NameNodeRpcServer裏面,拉到296行

再拉取到343行,發現又有一段相似的

還記得咱們剛纔說過,兩個服務一個是提供給內部的DataNode去調用,而另一個是提供給服務端去調用的,因此這裏這倆,第一個serviceRpcServer是服務於NameNode和DataNode之間調用的,而第二個clientRpcServer是服務於客戶端與NameNode,DataNode進行交互調用的

並且在建立它們以後,會有不少的協議被添加進來,這些協議也是帶有許許多多的方法的,添加的協議越多,這兩個服務的功能也就越強大

因此它們和HttpServer的套路是同樣的,它們是經過添加協議來加強本身,而HttpServer是經過添加servlet而已

1.7 流程圖的第二步

把NameNodeRpcServer的結構圖畫出,也就是主體爲 serviceRpcServer 和 clientRpcServer ,而後它們倆提供了各類各樣的服務方法

客戶端操做NameNode(好比建立目錄mkdirs)就要使用 clientRpcServer 提供的服務,而各個DataNode和NameNode的互相調用則經過 serviceRpcServer 實現

圖中的namenode能夠視爲standBy NameNode,在HA高可用中提到過的active和standby忘記的能夠去複習一下

1.8 正式啓動前的檢查

安全模式咱們在HDFS的第一篇中的心跳機制中已經提過了,這裏直接複製過來

hadoop集羣剛開始啓動時會進入安全模式(99.99%),就用到了心跳機制,其實就是在集羣剛啓動的時候,每個DataNode都會向NameNode發送blockReport,NameNode會統計它們上報的總block數,除以一開始知道的總個數total,當 block/total < 99.99% 時,會觸發安全模式,安全模式下客戶端就無法向HDFS寫數據,只能進行讀數據。

點進startCommonServices

1.8.1 NameNode資源檢查1 --- 獲取須要檢查的目錄

NameNodeResourceChecker直譯過來就是NameNode的資源檢查器,咱們點進去看到

duReserved

這裏是一個duReserved的值,它可讓咱們自行設置,若是不設置,就使用它給咱們的默認值。咱們也能夠查看一下這個DFS_NAMENODE_DU_RESERVED_DEFAULT的默認值,它爲100M,定義在DFSConfigKeys.java中

這裏我也能夠補充一下,咱們須要判斷告警的目錄就在下面的getNamespaceEditsDirs(conf)中,一直點進去就能夠看見咱們剛剛提到的,須要檢查的三個目錄(NameNode中存儲fsimage的目錄和edit log的目錄和JournalNode的目錄,這些參數所有都是定義在DFSConfigKeys.java中)

localEditDirs並非HDFS上的目錄,而是linux磁盤上的目錄,以後遍歷這個 localEditDirs 加入到一個volume中

在HDFS上不少位置均可以看到volumes這個單詞,volumes其實就是一個存放須要檢查的目錄的目錄集,註釋上的第一句話「Add the volume of the passed-in directory to the list of volumes to check.」意思就是將傳入目錄的卷添加到要檢查的卷列表中,這裏的卷就是volumes。

因此咱們如今回到startCommonServices,如今已經得知

// 做用就是獲取到全部須要檢查的目錄
nnResourceChecker = new NameNodeResourceChecker(conf);
複製代碼

1.8.2 NameNode資源檢查2 --- 檢查是否有足夠磁盤空間存儲元數據

checkAvailableResources() 點進去看一下

hadoop的方法命名老是如此地直白,hasAvailableDiskSpace直譯過來就是有沒有可用空間,再點擊進去

這裏看到 volumes 了,由於剛剛也解釋過了,volumes 就是存放須要進行檢查的目錄(也就是那3個目錄)的,那天然把 volumes 做爲參數傳進來,就把那三兄弟一鍋端了。再點進進去areResourcesAvailable方法,既然這個 volumes 是一個集合,那咱們打賭它裏面的邏輯確定有一個for循環來遍歷 volumes 裏面的那些值進行檢查

果不其然,for循環出現了。此時isResourceAvailable就是判斷依據了

這裏咱們看到,它先獲取到了當前目錄的大小(jdk),而後和咱們以前講到的 duReserved (默認100M)作比較。這時候咱們就把前面的知識點串起來了。

若是空間不足,它就會打印一段話,這段話在咱們平常的公司集羣環境中不太可能看到,可是若是咱們是本身搭建的集羣進行學習時就會看到,這個時候就是咱們的虛擬機的空間不足,雖然集羣服務正常啓動,但是集羣沒法正常工做。

1.8.3 NameNode資源檢查3 --- 安全模式的檢查

面試題:咱們是否真的清楚爲何hadoop集羣會進入安全模式呢?

回到FSNameSystem,點進去 setBlockTotal() 方法

點進去看看它是如何獲取正常block個數的

獲取正在構建的block是個啥意思呢,再繼續點進去

在HDFS裏面block有兩種狀態,一種是complete,已是可使用的一個完整block,還有一種是UnderConstruct,這個屬於正在構建,還不能正常使用,對應下文中的這個代碼

因此咱們退回來,getCompleteBlocksTotal中,使用blockTotal總block數-numUCBlocks得出來的就是準備complete的block個數

退回到setBlockTotal中的safeMode.setBlockTotal((int)getCompleteBlocksTotal()),點進去setBlockTotal

這裏就是設置什麼時候能夠退出安全模式的代碼,threshold默認值爲0.999,好比咱們總block數爲1000,那若是存在999個isComplete的block,也就認爲集羣是健康狀態,而能夠退出安全模式。

點進去checkMode(),咱們能夠發現needEnter()便是判斷進入安全模式的條件(剛剛說明的是退出的)。

任意知足如下3個條件的任意一個,都會進去安全模式

第一個條件

threshold != 0沒啥好說的,這個東西自己就不可能設置0,blockSafe < blockThreshold 這裏的blockSafe是指什麼呢?

咱們的集羣啓動時,NameNode啓動後DataNode也會啓動,在DataNode啓動時會向NameNode上報本身的block狀態信息,每上報一個,blockSafe就會加一。blockThreshold知足安全模式閾值條件所需的塊數,當上報的量比安全模式要求的量少時,安全模式啓動。

第二個條件

datanodeThreshold != 0 && getNumLiveDataNodes() < datanodeThreshold

DataNode和NameNode會有心跳機制,這個條件大概就是,集羣中存活的DataNode個數少於datanodeThreshold個就讓集羣處於安全模式

很是搞笑的是,這個datanodeThreshold的默認值是0,還須要本身配置,因此這個條件基本本身不配置是不生效的。hhh

第三個

!nameNodeHasResourcesAvailable()直接翻譯過來,磁盤空間是否充足,對,引用的就是上面提過的hasAvailableDiskSpace,直譯過來就是有沒有可用空間.

1.9 服務啓動

感動,整了這麼久終於能夠正常啓動了

固然這裏會連帶啓動一些服務,後面再展開了

1.10 流程圖的第三步

其實就是補充了FSNameNode,而後把前幾回畫的都放在一塊兒而已

finally

這一篇就是NameNode的啓動的大體流程了,固然有不少細節的部分咱們仍未深究或者是一筆帶過,這些細節有些不重要有些是以後再進行補充的,截圖真的是很是麻煩,但願這些不管是對你,對我都會有所收穫。

相關文章
相關標籤/搜索