上篇內容分析了http server的啓動代碼,這篇文章繼續從initialize()方法中按執行順序進行分析。內容仍是分爲三大塊:node
1、源碼調用關係分析安全
2、僞代碼執行流程函數
3、代碼圖解this
1、源碼調用關係分析spa
上一篇內容是NameNode啓動http server的分析,是根據鎖定NameNode的main()入口,發現了該入口僅有兩行核心代碼,先進入到了第一行核心代碼日誌
createNameNode()中,發現默認狀況是new了一個NameNode對象。在NameNode的構造方法中,有一些很重要的初始化操做,好比啓動code
http server、加載元數據、初始化rpc server、安全模式檢查等。server
廢話很少說,前面的NameNode.main()、createNameNode()、new NameNode()都再也不贅述、直接從initialize()提及:xml
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); ...... // 核心代碼:啓動一些服務組件,包括rpc server等 startCommonServices(conf); }
本篇內容主要是關於管理磁盤元數據的FSNamesystem初始化流程,因此從方法名便可推測loadNamesystem()是須要咱們重點關注的核心中的核心。對象
點進去:
protected void loadNamesystem(Configuration conf) throws IOException { // 核心代碼:從磁盤上加載元數據,loadFromDisk()就是從磁盤上讀取fsimage和edits文件。 this.namesystem = FSNamesystem.loadFromDisk(conf); }
咱們都知道NameNode的功能之一是,在NameNode啓動的時候,會將磁盤上的fsimage和edits兩個文件都讀取到內存中進行合併,造成一份最新的
元數據。這份最新的元數據放在哪呢?實際上是放在了內存中由FSNamesystem進行管理。真的是這樣的嗎?咱們繼續往下看,進入到loadFromDisk():
/** * Instantiates an FSNamesystem loaded from the image and edits * directories specified in the passed Configuration. * * @param conf the Configuration which specifies the storage directories * from which to load * @return an FSNamesystem which contains the loaded namespace * @throws IOException if loading fails */ static FSNamesystem loadFromDisk(Configuration conf) throws IOException { checkConfiguration(conf); // 核心代碼:構造一個FSImage對象,從NamespaceDirs、NamespaceEditsDirs指定的路徑加載 FSImage fsImage = new FSImage(conf, // 默認是從 DFS_NAMENODE_NAME_DIR_KEY 加載fsimage文件 FSNamesystem.getNamespaceDirs(conf), // 默認是從 DFS_NAMENODE_EDITS_DIR_KEY 加載edits文件 FSNamesystem.getNamespaceEditsDirs(conf)); // 核心代碼:根據指定了fsimage和edits文件路徑的fsimage對象,實例化FSNamesystem對象 FSNamesystem namesystem = new FSNamesystem(conf, fsImage, false); StartupOption startOpt = NameNode.getStartupOption(conf); if (startOpt == StartupOption.RECOVER) { namesystem.setSafeMode(SafeModeAction.SAFEMODE_ENTER); } ...try { // 核心代碼:從磁盤上加載數據,在內存中合併數據 // 在NameNode剛啓動的時候,會從磁盤讀取fsimage和edits文件到內存中,合併爲一份最新的 // 元數據,而後將這份新的元數據寫出到磁盤替換以前舊的fsimage。而後,還會從新打開一個 // 空的edits文件,以供接下來的元數據變更日誌寫入。這個loadFSImage()主要就是幹了這麼一件事情 // 會在namenode元數據管理研究時進行深刻詳細剖析。 namesystem.loadFSImage(startOpt); } catch (IOException ioe) { ... }
... return namesystem; }
首先這個loadFromDisk()有一段註釋,將這個方法的功能和目的說的也很明白了:根據配置文件指定的路徑,實例化一個從image和edits加載元數據
的FSNamesystem對象。下面開始分析核心代碼,這段有3行核心代碼:
fsimage = new FSImage(conf, NameSpaceDirs, NameSpaceEditsDirs);
這行代碼是構造了一個FSImage對象,從NameSpaceDirs, NameSpaceEditsDirs分別
讀取fsimage和edits文件。其中NameSpaceDirs和NameSpaceEditsDirs通過溯源分別對應 dfs.namenode.name.dir 和
dfs.namenode.shared.edits.dir,這兩個屬性均可以在hdfs-default.xml中查看和配置。
FSNamesystem namesystem = new FSNamesystem(conf, fsimage, false);
根據上面指定了fsimage路徑和edits路徑的fsimage對象,構造一個FSNamesystem對象。
namesystem.loadFSImage();
開始執行loadFSImage()方法,從磁盤上加載數據並在內存中進行合併。
注意,在合併fsimage和edits造成一份新的元數據以後,會將這份新的元數據寫出到磁盤替換舊的fsimage文件,而後還會打開一個新的edits
文件,以供接下來的元數據變更的日誌寫入。這些操做都是在loadFSImage()中完成的,具體的進入到該方法中能夠詳細瞭解。
2、僞代碼執行流程
NameNode.main() // 入口函數
|——createNameNode(); // 經過new NameNode()進行實例化
|——initialize(); // 方法進行初始化操做
|——startHttpServer(); // 啓動HttpServer
|——loadNamesystem(); // 加載元數據
|——loadFromDisk(); // 從磁盤加載數據
|——new FSImage(); // 實例化FSImage對象
|——new FSNamesystem(fsImage); // 根據FSImage對象實例化FSNamesystem
|——loadFSImage(startOpt); // 加載併合並fsimage、edits,而後寫出到磁盤
|——join()
3、代碼圖解