代碼流程:
NameNode.main() // 入口函數 |——createNameNode(); // 通過new NameNode()進行實例化 |——initialize(); // 方法進行初始化操作 |——startHttpServer(); // 啟動HttpServer |——loadNamesystem(); // 加載元數據 |——createRpcServer(); // 創建並初始化rpc server實例 |——startCommonServices(); |——namesystem.startCommonServices(); // 啟動一些磁盤檢查、安全模式等一些后台服務及線程 |——new NameNodeResourceChecker(); // 實例化一個NameNodeResourceChecker並准備出所有需要檢查的磁盤路徑 |——checkAvailableResources(); // 開始磁盤空間檢查 |——NameNode.getStartupProgress(); // 獲取StartupProgress實例用來獲取NameNode各任務的啟動信息 |——setBlockTotal(); // 設置所有的block,用於后面判斷是否進入安全模式 |——blockManager.activate(); // 啟動BlockManager里面的一堆關於block副本處理的后台線程 |——rpcServer.start(); // 啟動rpcServer |——join()
所以直接查看startCommonServices代碼:
1、總體介紹
1、 檢查資源情況、判斷是否進入安全狀態、激活BlockManager
2、啟動RPC服務
1 /** 2 * 1、 namesystem.startCommonServices(conf, haContext); 3 * 檢查資源情況、判斷是否進入安全狀態、激活BlockManager 4 * 2、啟動RPC服務 5 * */ 6 private void startCommonServices(Configuration conf) throws IOException { 7 //FSNamesystem是NameNode核心成員變量用來管理元數據(實現對DataNode、Block的管理以及讀寫日志) 8 //創建NameNodeResourceChecker、激活BlockManager等 9 namesystem.startCommonServices(conf, haContext); 10 registerNNSMXBean();//注冊一個namenode的狀態類 11 // 角色非`NamenodeRole.NAMENODE`的在此處啟動HttpServer 12 if (NamenodeRole.NAMENODE != role) { 13 startHttpServer(conf); 14 httpServer.setNameNodeAddress(getNameNodeAddress()); 15 httpServer.setFSImage(getFSImage()); 16 } 17 //啟動服務 18 rpcServer.start();// 19 plugins = conf.getInstances(DFS_NAMENODE_PLUGINS_KEY, 20 ServicePlugin.class); 21 for (ServicePlugin p: plugins) { 22 try { 23 p.start(this); 24 } catch (Throwable t) { 25 LOG.warn("ServicePlugin " + p + " could not be started", t); 26 } 27 } 28 LOG.info(getRole() + " RPC up at: " + rpcServer.getRpcAddress()); 29 if (rpcServer.getServiceRpcAddress() != null) { 30 LOG.info(getRole() + " service RPC up at: " 31 + rpcServer.getServiceRpcAddress()); 32 } 33 }
2、namesystem.startCommonServices(conf, haContext);檢查資源
整體大致邏輯:
1、將需要檢查的URL添加到volumes中 , 后台有線程會一直執行hasAvailableDiskSpace來檢查 2、checkAvailableResources(); 進行資源檢查 3、NameNode啟動,進入到safemode階段,處於一個等待匯報blocks的狀態 4、匯報所有的block,用於后面判斷是否進入安全模式 5、激活BlockManager
/** * 1、將需要檢查的URL添加到volumes中 , 后台有線程會一直執行hasAvailableDiskSpace來檢查 * 2、checkAvailableResources(); 進行資源檢查 * 3、NameNode啟動,進入到safemode階段,處於一個等待匯報blocks的狀態 * 4、匯報所有的block,用於后面判斷是否進入安全模式 * 5、激活BlockManager */ void startCommonServices(Configuration conf, HAContext haContext) throws IOException { this.registerMBean(); // register the MBean for the FSNamesystemState writeLock(); this.haContext = haContext; try { //NameNodeResourceChecker負責檢查磁盤資源。 // active狀態的namenod會啟動一個監控線程NameNodeResourceMonitor, // 定期執行NameNodeResourceChecker#hasAvailableDiskSpace()檢查可用的磁盤資源。 /**需要檢查3個涉及到元數據的目錄: * Namenode2個目錄:fsimage、editlog(默認情況下這兩個是在同一個目錄) * 高可用模式下的journalNode里面也有存儲袁術的目錄 * */ nnResourceChecker = new NameNodeResourceChecker(conf); //檢查可用資源是否足夠:如果不夠,日志打印警告信息,然后進入安全模式 checkAvailableResources(); // 判斷是否進入安全模式,並且副本隊列是否應該被同步/復制 /** * 磁盤資源不足的情況下,任何對元數據修改所產生的日志都無法確保能夠寫入到磁盤, * 即新產生的edits log和fsimage都無法確保寫入磁盤。所以要進入安全模式, * 來禁止元數據的變動以避免往磁盤寫入新的日志數據 * */ assert safeMode != null && !isPopulatingReplQueues(); //獲取StartupProgress實例用來獲取NameNode各任務的啟動信息 StartupProgress prog = NameNode.getStartupProgress(); // 目前NameNode啟動,進入到safemode階段,處於一個等待匯報blocks的狀態 prog.beginPhase(Phase.SAFEMODE); //處於一個等待匯報blocks的狀態 prog.setTotal(Phase.SAFEMODE, STEP_AWAITING_REPORTED_BLOCKS, getCompleteBlocksTotal()); //設置所有的block,用於后面判斷是否進入安全模式 setBlockTotal(); //TODO 啟動BlockManager里面關於block副本處理的后台線程 //激活BlockManager blockManager.activate(conf); } finally { writeUnlock(); } registerMXBean(); DefaultMetricsSystem.instance().register(this); if (inodeAttributeProvider != null) { inodeAttributeProvider.start(); dir.setINodeAttributeProvider(inodeAttributeProvider); } snapshotManager.registerMXBean(); }
3、NameNodeResourceChecker檢查資源類
addDirToCheck方法:
還有就是,在NameNodeResourceChecker構造方法中,我們得到了duReserved是100M,那么他在哪里使用的?
1):
2):
3):
最后就是areResourcesAvailable這個方法,主要對volumns里面的url進行檢查,看看這些url路徑是否可用,是否滿足繼續運行的最小資源數
其中的isResourceAvailable方法就是檢查磁盤空間的:
最后在計算是否滿足繼續運行需要的最少數量
然后程序返回到NameNodeResourceMonitor這個監控線程地方:
所以說這個NameNodeResourceChecker(磁盤資源檢查類),主要是干了3件事:
1、聲明namenode容忍的磁盤大小的閾值(100M)
2、封裝好需要檢查的磁盤路徑
3、將需要檢查的磁盤路徑通過addDirToCheck方法添加到volumes這個map集合里面, 然后在FSNameSystem中有一個NameNodeResourceMonitor線程,不斷的調用checkAvailableResources方法 來檢查volumes(磁盤的資源情況)
4):checkAvailableResources
這個checkAvailableResources就是剛剛講解的;
只不過在初始化nameNode的時候會主動檢查一次,啟動后就會通過NameNodeResourceMonitor這個線程不斷的去檢查(每隔1秒去檢查一遍)
ok,這個關於磁盤資源檢查的部分說完了,接下來就是看看startCommonServices方法里面剩余部分
5):剩下的部分
通過StartUpPrpcess(NameNode任務的啟動信息)來指示namenode的運行狀態,namenode啟動后,首先是進入安全模式,然后等待blocks狀態匯報,只有blocks滿足了最低指標需求(0.999f),才會退出安全模式
6):getCompleteBlocksTotal
這里面有個關鍵點就是,通過prog.setTotal來匯報blocks的狀態,那么blocks的狀態怎么拿到?
然后拿到總數據塊 - 無法讀取的數據塊 = 目前可用的數據塊
緊接着就是getNumUnderConstructionBlocks
這段代碼就是獲取非Complete
此處處理完畢之后,返回看setBlockTotal()方法
7):setBlockTotal()
設置所有的block,用於后面判斷是否進入安全模式
注意其中的blockSafe:是datanode向namenode進行匯報的塊個數,通過incrementSafeBlockCount方法,不斷的疊加起來的
當datanode向namenode匯報刪除數據塊的時候,此處就對blockSafe減小
其中還有一個關鍵點就是checkMode
用於檢查安全模式的狀態: 1、判斷閾值系數是否滿足進入安全模式:needEnter 對於離開安全模式,有兩個條件判斷: 1、判斷系數是否滿足離開安全模式 2、啟動SafeModeMonitor線程,每隔1秒去查看下,是否可以退出安全模式
如果要是進入了安全模式,那么這個enter()方法里面會把this.reached = 0;
看下needEnter()方法
所以需要看一下,是否需要進入安全模式的條件:needEnter()
其次就是在checkMode中,除了進入安全模式以外,還有退出安全模式的邏輯
10):后台線程:SafeModeMonitor的代碼
這個SafeModeMonitor里面這個 判斷是否能夠退出安全模式的依據就是:
canLeave代碼
然后在看下canLeave的代碼邏輯:
ok。這樣我們整體的startCommonServices代碼就完事了;
總體namenode的啟動流程: