Hadoop源碼分析-NameNode 啟動(dòng)流程源碼深度剖析

本文嘗試講解 NameNode 啟動(dòng)流程源碼剖析,內(nèi)容包含

  1. Namenode 啟動(dòng)流程跟蹤
  2. Namenode Httpserver 啟動(dòng)流程跟蹤
  3. Namenode 元數(shù)據(jù)加載跟蹤
    閱讀本文,必須了解 HDFS2 的架構(gòu)以及Hadoop RPC 的使用

開(kāi)始之前先簡(jiǎn)單說(shuō)下閱讀源碼的技巧

  1. 先掌握其通信架構(gòu) - Hadoop RPC
  2. 場(chǎng)景驅(qū)動(dòng)的方式閱讀 - 弄懂核心場(chǎng)景
  3. 邊看源碼,邊畫(huà)圖,邊寫(xiě)自己的注釋
  4. 看類(lèi)本身的注釋
  5. 有些代碼實(shí)在沒(méi)有思路,就只能猜了

一、NameNode 啟動(dòng)流程源碼深度剖析

1. NameNode.java 類(lèi)注釋

image

以下是 Namenode 類(lèi)的注釋?zhuān)约昂?jiǎn)單的翻譯(翻譯并不完全是按照原文翻譯的)

NameNode serves as both directory namespace manager and
"inode table" for the Hadoop DFS.  There is a single NameNode
running in any DFS deployment.  (Well, except when there
is a second backup/failover NameNode, or when using federated NameNodes.)

NameNode 即是 Hadoop DFS 的目錄命名空間管理器,也“inode表”。
在 HDFS 集群中只有一個(gè) Namenode(除了 HA和聯(lián)邦外);

The NameNode controls two critical tables:
  1)  filename->blocksequence (namespace)
  2)  block->machinelist ("inodes")

Namenode 控制 2 個(gè)表:

  1. 文件名-> block 塊(命名空間)
  2. blcok -> 機(jī)器列表(inodes)
 The first table is stored on disk and is very precious.
 The second table is rebuilt every time the NameNode comes up.

第一個(gè)表存儲(chǔ)在磁盤(pán)上,原因是它很重要(還有就是 文件名與block的關(guān)系,基本不會(huì)改變)
第二個(gè)表每次 Namenode 啟動(dòng)的時(shí)候重新構(gòu)建(block與機(jī)器的關(guān)系,可能會(huì)改變,如果增減機(jī)器等)

 NameNode refers to both this class as well as the NameNode server.
 The FSNamesystem class actually performs most of the filesystem
 management.  The majority of the NameNode class itself is concerned
 with exposing the IPC interface and the HTTP server to the outside world,
 plus some configuration management.

NameNode 既指這個(gè)類(lèi),也指NameNode server。FSNamesystem 類(lèi)實(shí)際上執(zhí)行了大部分文件系統(tǒng)管理工作。
NameNode 類(lèi) 對(duì)外公開(kāi)了 IPC 接口(RPC服務(wù)) 和 HTTP 服務(wù)(50070),以及一些配置管理。

2. NameNode 啟動(dòng)代碼猜測(cè)

在我們開(kāi)始 NameNode 啟動(dòng)代碼分析之前,我們先對(duì) NameNode 代碼進(jìn)行一個(gè)猜測(cè)。
我們之前說(shuō)了Namenode 肯定是個(gè)RPC服務(wù),那么它就符合 RPC的規(guī)范,那么我們就可以模擬下 NameNode的代碼

//協(xié)議
public interface A{
    public long versionID=xxxxL;
    public void xxx();
}
public interface B{
    public long versionID=yyyyL;
    public void yyy();
}

//服務(wù)端
public class NameNode implements A,B{
    public void xxx(){
      ......
    }
         public void xxx(){
      ......
    }
    public static void main(String args[]){
        RPC.Server server = new RPC.Builder(new Configuration())
        .setBindAddress(xxxx)
        .setPort(xxxx)//9000/8020
        .setProtocol(A.class)
        .setInstance(new NameNode())
        .build();
        server.start();
    }
}

3. NameNode 啟動(dòng)代碼分析

  1. 找到 Namenode 啟動(dòng)的入口(也就是main方法),從字面上看, NameNode namenode = createNameNode(argv, null); 應(yīng)該就是創(chuàng)建一個(gè) Namenode ,既然是創(chuàng)建 Namendoe 應(yīng)該跟啟動(dòng)有很大的關(guān)系了,而其他的代碼就看著就關(guān)系不大。
image
  1. 進(jìn)入 createNameNode 的代碼后,看到一個(gè) switch,仔細(xì)查看各分支后,發(fā)現(xiàn)除了 default 外,其他都跟啟動(dòng)沒(méi)有關(guān)系。而default 這里就是 new 了一個(gè) NameNode

    namenode啟動(dòng)入口

  2. 跟進(jìn) new NameNode(conf); 代碼后,可以看到 try 前面的代碼都是給變量賦值,在 try 中 initialize(conf); 比較可疑;

    image

  3. 跟進(jìn) initialize(conf);后,發(fā)現(xiàn) rpcServer = createRpcServer(conf); 代碼,這個(gè)代碼非常直觀(guān)的說(shuō)明,這里就是 RPC 服務(wù);

    image

  4. 跟進(jìn) rpcServer = createRpcServer(conf); 后,發(fā)現(xiàn)就只有一行代碼new NameNodeRpcServer(conf, this);,這個(gè)就是說(shuō) new一個(gè) NameNodeRpcServer 對(duì)象,直接跟進(jìn)

    image

  5. 跟進(jìn) new NameNodeRpcServer(conf, this); 后,會(huì)看到一堆 new,new完也沒(méi)干什么,那么我們就把代碼往下拉,看看有沒(méi)有其他的不是賦值的。

    image

  6. 終于,我們看到了 RPC.Builder 代碼。雖然我們找到了 RPC.Builder 代碼,但是這跟我們之前的猜想有些不一樣,我們之前猜想 RPC.Builder 代碼 應(yīng)該是在 Namenode 中,而實(shí)際上 RPC.Builder 是在 NameNodeRpcServer 中。

    image

  7. 再來(lái)看看 Namenode 實(shí)現(xiàn)的接口,也不像我們之前說(shuō)的 RPC 的規(guī)范,最起碼來(lái) versionID 字段都沒(méi)有。

    image

  8. 我們?cè)倏纯?NameNodeRpcServer 實(shí)現(xiàn)的接口,發(fā)現(xiàn) NameNodeRpcServer 實(shí)現(xiàn)的接口又繼承了很多個(gè)接口

    image

  9. 我們隨便選幾個(gè)繼承的接口看看


    image

    image
  10. 發(fā)現(xiàn)這些都有 versionID 字段,那么就可以得出結(jié)論,NameNodeRpcServer 是RPC 的服務(wù)端,而 Namenode 不是。
    但是呢,我們又在 JPS 的時(shí)候明明看到了 Namenode ,這是什么原因呢?
    我們到 Namenode 中搜索一下 NameNodeRpcServer 發(fā)現(xiàn)原來(lái),NameNodeRpcServer 是Namenode的一個(gè)成員變量。

    image

  11. 并且 NameNodeRpcServer 的賦值和啟動(dòng)都是在 Namenode 中進(jìn)行的。


    image

4. NameNode 啟動(dòng)代碼總結(jié)

根據(jù)實(shí)際的源碼分析,我們可以修改下我們之前的代碼猜測(cè)

//協(xié)議
public interface A{
    public long versionID=xxxxL;
    public void xxx();
}
public interface B{
    public long versionID=yyyyL;
    public void yyy();
}

public interface C extends A,B{

}

//服務(wù)端
public class NameNodeRpcServer implements C{
    public void xxx(){
      ......
    }
         public void xxx(){
      ......
    }
            RPC.Server server = new RPC.Builder(new Configuration())
            .setBindAddress(xxxx)
            .setPort(xxxx)//9000/8020
            .setProtocol(A.class)
            .setInstance(new NameNode())
            .build();
            server.start();
}
public class NameNode {
    public static void main(String args[]){
             NameNodeRpcServer rpcServer = createRpcServer(conf);// TODO 創(chuàng)建 RPC 服務(wù)
             rpcServer.start()
         }
}

5. NameNode Httpserver 啟動(dòng)跟蹤

NameNode 服務(wù)啟動(dòng)跟蹤 的第 4 步,我們還看到 Namenode 啟動(dòng)了一個(gè) HTTP 服務(wù),我們簡(jiǎn)單來(lái)看下這個(gè)代碼:

image

  1. 我們來(lái)跟進(jìn)下這個(gè)HTTP服務(wù)代碼,可以看到這里 new 了一個(gè) NameNodeHttpServer,然后又調(diào)用了一個(gè) start 方法

    image

  2. 我們看看 httpServer = new NameNodeHttpServer(conf, this, getHttpServerBindAddress(conf)); 里面做了什么??磥?lái)里面就只是賦值,并沒(méi)有其他的操作。那么我們?cè)诳纯?new 之后的 start 方法

    image

  3. 在 start 方法中,我們看到了,構(gòu)建 HttpServer2 服務(wù)的代碼,這個(gè) HttpServer2 跟 JDK 的 HttpServer 差不多,他是 Hadoop 自己封裝的,具體我們就不看了。 注意 setupServlets(httpServer, conf);,這就是 綁定 servlet。

    image

  4. 我們跟進(jìn) setupServlets(httpServer, conf);看到這里的確綁定了很多 servlet。

    image

6. Namenode 元數(shù)據(jù)加載源碼跟蹤(簡(jiǎn)述)

NameNode 服務(wù)啟動(dòng)跟蹤 的第 4 步,我們還看到 加載元數(shù)據(jù)的代碼,我們也簡(jiǎn)單的先看看

image

  1. 跟進(jìn)去可以看到就一行代碼,我們繼續(xù)跟進(jìn)


    image
  2. 再跟進(jìn)去,可以看到下面代碼,我們可以看到這里 new 了一個(gè) Fsimage 對(duì)象,雖然沒(méi)有使用,但是發(fā)現(xiàn) namesystem.loadFSImage(startOpt); 代碼的名字很像加載 Fsimage。我們繼續(xù)看看
    image
  3. 跟進(jìn)去后,第一句就是 獲得 getFSImage(),這個(gè)返回的就是第2不里 new 的 fsimage 對(duì)象。然后就是對(duì) fsimage 進(jìn)行格式化(如果是第一次啟動(dòng)的話(huà)),接著后面的一句代碼 fsImage.recoverTransitionRead(startOpt, this, recovery); 這個(gè)就是合并 fsimage 和 edit ,再然后就是存儲(chǔ)到 新的 fsimage 到磁盤(pán)等,具體的我們以后再細(xì)講
    image

7. Namenode 啟動(dòng)源碼總結(jié)

  1. 先啟動(dòng) httpserver
  2. 加載元數(shù)據(jù)
  3. 啟動(dòng) RPC 服務(wù)
    NameNode 啟動(dòng)流程源碼深度剖析

    PS:其實(shí)在啟動(dòng) RPC服務(wù)之前還有檢查資源和判斷是否進(jìn)入安全模式,這塊我們以后再講
    點(diǎn)擊這里查看原文地址
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容