【hadoop二次開發】根據彙報的資料塊判斷是否可以離開安全模式
008-hadoop二次開發
在原始碼檔案FSNamesystem.java執行完nnResourceChecker = new NameNodeResourceChecker(conf);
立馬執行checkAvailableResources(),檢查可用資源是否足夠:如果不夠,日誌列印警告資訊,然後進入安全模式。
然後
/**
* 磁碟資源不足的情況下,任何對後設資料修改所產生的日誌都無法確保能夠寫入到磁碟,
* 即新產生的edits log和fsimage都無法確保寫入磁碟。所以要進入安全模式,
* 來禁止後設資料的變動以避免往磁碟寫入新的日誌資料
* */
assert safeMode != null && !isPopulatingReplQueues();
/**
* Check if replication queues are to be populated
* @return true when node is HAState.Active and not in the very first safemode
*/
/**這個方法是用來檢查副本佇列是否應該被同步/複製*/
@Override
public boolean isPopulatingReplQueues() {
if (!shouldPopulateReplQueues()) {
return false;
}
return initializedReplQueues;
}
設定資料塊彙報
//處於一個等待彙報blocks的狀態
prog.setTotal(Phase.SAFEMODE, STEP_AWAITING_REPORTED_BLOCKS, getCompleteBlocksTotal());
/**
* NN端的block有四種狀態:
* 1、UnderConstruction(正在被寫入的狀態)
* 2、UnderRecovery(正在被恢復的塊)
* 3、Committed(已經確定好它的位元組大小與generation stamp值(可理解為版本號))
* 4、Complete(寫入執行操作結束狀態)
* 啟動的時候 ,namenode認為只有block狀態為Complete,才會被讀取
*
* 獲取系統中的COMPLETE塊總數。
* 對於安全模式,僅計算完整塊。
*/
private long getCompleteBlocksTotal() {
// Calculate number of blocks under construction
long numUCBlocks = 0;
readLock();
//獲取所有正在構建的block塊
//leaseManager 檔案租約(為每一個客戶端和檔案構建關聯)
numUCBlocks = leaseManager.getNumUnderConstructionBlocks();
try {
return getBlocksTotal() - numUCBlocks;
} finally {
readUnlock();
}
}
設定所有的block,用於後面判斷是否進入安全模式setBlockTotal(),
/**
* Set the total number of blocks in the system.
*/
public void setBlockTotal() {
// safeMode is volatile, and may be set to null at any time
SafeModeInfo safeMode = this.safeMode;
if (safeMode == null)
return;
//TODO 設定安全模式
//getCompleteBlocksTotal 獲取所有正常使用的block個數
safeMode.setBlockTotal((int)getCompleteBlocksTotal());
}
通過SafeModeInfo safeMode = this.safeMode;拿到狀態資訊,
/** Time when threshold was reached.
* <br> -1 safe mode is off
* <br> 0 safe mode is on, and threshold is not reached yet
* <br> >0 safe mode is on, but we are in extension period
*/
private long reached = -1;
/**
* 1、得到滿足安全模式閾值條件所需的塊數
* 2、填充複製佇列之前所需的塊數
* 3、檢查是否需要進入安全模式
*/
private synchronized void setBlockTotal(int total) {
this.blockTotal = total;//彙報過來的blocks(complete狀態)個數
//滿足安全模式閾值條件所需的塊數 blockTotal * 0.999f 1000 * 0.999 = 999
this.blockThreshold = (int) (blockTotal * threshold);
//填充複製佇列之前所需的塊數 blockTotal * 0.999f
this.blockReplQueueThreshold = (int) (blockTotal * replQueueThreshold);
if (haEnabled) {
// After we initialize the block count, any further namespace
// modifications done while in safe mode need to keep track
// of the number of total blocks in the system.
this.shouldIncrementallyTrackBlocks = true;
}
/**
* blockSafe是datanode向namenode進行彙報的塊個數,通過incrementSafeBlockCount方法,不斷的疊加起來的
* 通過decrementSafeBlockCount,當datanode向namenode彙報刪除資料塊的時候,此處就對blockSafe減小
* */
if(blockSafe < 0)
this.blockSafe = 0;
checkMode();
}
/**
* 用於檢查安全模式的狀態:
* 1、判斷閾值係數是否滿足進入安全模式:needEnter
* 對於離開安全模式,有兩個條件判斷:
* 1、判斷係數是否滿足離開安全模式
* 2、啟動SafeModeMonitor執行緒,每隔1秒去檢視下,是否可以退出安全模式
*/
private void checkMode() {
/**assert 如果<boolean表示式>為true,則程式繼續執行。
如果為false,則程式丟擲AssertionError,並終止執行。*/
assert hasWriteLock();//安全模式下,需要寫鎖,禁止寫入
//如果當前節點已經是active狀態,則不需要在檢查了,直接返回
if (inTransitionToActive()) {
return;
}
//TODO 判斷是否進入安全模式
if (smmthread == null && needEnter()) {
//進入安全模式
enter();
/**
* canInitializeReplQueues:判斷namenode已經接收到的blockSafe塊數量是否達到了
* 恢復複製和刪除資料塊資料塊數量
* */
if (canInitializeReplQueues() && !isPopulatingReplQueues()
&& !haEnabled) {
initializeReplQueues();
}
reportStatus("STATE* Safe mode ON.", false);
return;
}
/**
* extension處於安全模式的時間(滿足了退出安全模式條件 ,此時還是處於安全模式的時間)
* threshold安全係數
* isOn 當前的狀態(reached)是否小於0
* */
if (!isOn() || extension <= 0 || threshold <= 0) { // don't need to wait
this.leave(); // 退出安全模式
return;
}
if (reached > 0) { // threshold has already been reached before
reportStatus("STATE* Safe mode ON.", false);
return;
}
/**TODO 啟動SafeModeMonitor執行緒,每隔1秒去檢視下,是否可以退出安全模式*/
reached = monotonicNow();
reachedTimestamp = now();
if (smmthread == null) {
smmthread = new Daemon(new SafeModeMonitor());
smmthread.start();
reportStatus("STATE* Safe mode extension entered.", true);
}
// check if we are ready to initialize replication queues
if (canInitializeReplQueues() && !isPopulatingReplQueues() && !haEnabled) {
initializeReplQueues();
}
}
是否進入安全模式的enter方法
/**
* Enter safe mode.
*/
private void enter() {
/**
* reached
* <br> -1 退出安全模式
* <br> 0 進入安全模式
* */
this.reached = 0;
this.reachedTimestamp = 0;
}
怎麼進入安全模式?
首先守護執行緒smmthread為空,並且needEnter()為真,
/**
* There is no need to enter safe mode
* if DFS is empty or {@link #threshold} == 0
*/
/**
* 進入安全模式有3個條件,任何一個滿足,都會進入安全模式
* 條件1:threshold != 0 && blockSafe < blockThreshold)
* this.blockThreshold = (int) (blockTotal * threshold);
* 也就是說:如果有1000個block,那麼閾值blockThreshold=999
* 但是如果叢集啟動,datanode彙報過來的累計的塊數小於<999,那麼就會進入安全模式
* 條件2:datanodeThreshold != 0 && getNumLiveDataNodes() < datanodeThreshold
* 啟動叢集后,存活的datanode個數小於datanodeThreshold(預設0),則進入安全模式
*
* 條件3:!nameNodeHasResourcesAvailable()
* 檢查磁碟,是否大於100M
*
* */
private boolean needEnter() {
//假如1000個資料塊 , blockSafe < 999
return (threshold != 0 && blockSafe < blockThreshold) ||
(datanodeThreshold != 0 && getNumLiveDataNodes() < datanodeThreshold) ||
(!nameNodeHasResourcesAvailable());
}
是否離開安全模式中的isOn方法
/**
* Check if safe mode is on.
* @return true if in safe mode
*/
private synchronized boolean isOn() {
doConsistencyCheck();
//安全模式化 reached = 0
//非 reached = -1
return this.reached >= 0;
}
接下來建立守護執行緒
進入SafeModeMonitor執行緒,實現了Runnable介面,在run方法中每個1s鍾迴圈檢查是否要退出安全模式,整體加了寫鎖,最後釋放寫鎖。
@Override
public void run() {
while (fsRunning) {
writeLock();
try {
if (safeMode == null) { // Not in safe mode.
break;
}
//TODO 判斷是否滿足退出安全模式
if (safeMode.canLeave()) {
// Leave safe mode.
safeMode.leave();//方法中將 reached = -1;
smmthread = null;//將守護執行緒置空
break;
}
} finally {
writeUnlock();
}
try {
Thread.sleep(recheckInterval);//TODO 每隔1s檢查一遍是否滿足條件
} catch (InterruptedException ie) {
// Ignored
}
}
if (!fsRunning) {
LOG.info("NameNode is being shutdown, exit SafeModeMonitor thread");
}
}
}
若退出安全模式,需滿足safeMode.canLeave()為true,
/**
* 通過3個條件來判斷是否可以離開安全模式:
* 1、reached是否為-1
* 2、在滿足最小副本條件之後,namenode還需要處於安全模式的時間(30s)
* 3、needEnter裡面的3個條件
* */
private synchronized boolean canLeave() {
//reached == -1是離開safemode
if (reached == 0) {
return false;
}
//extension 預設30s,也就是滿足最低副本系數之後,離開安全模式的時間,這個時間用於等待剩餘資料節點的資料塊上報
//這裡的reached是來自於建立守護執行緒前reached = monotonicNow();記錄一個當前時間
//此處的monotonicNow()又拿到一個當前時間
if (monotonicNow() - reached < extension) {
reportStatus("STATE* Safe mode ON, in safe mode extension.", false);
return false;
}
if (needEnter()) {
reportStatus("STATE* Safe mode ON, thresholds not met.", false);
return false;
}
return true;
}
monotonicNow()保障時間的準確性
public static long monotonicNow() {
final long NANOSECONDS_PER_MILLISECOND = 1000000;
return System.nanoTime() / NANOSECONDS_PER_MILLISECOND;
}
最後
//TODO 啟動BlockManager裡面關於block副本處理的後臺執行緒
//啟用BlockManager
blockManager.activate(conf);
相關文章
- 新手發問 ! Laravel admin 根據ID判斷資料Laravel
- NX二次開發-判斷程式是否為空刀軌
- iOS開發-如何判斷手機是否開啟了放大模式iOS模式
- 根據連線的資料庫判斷資料庫型別(JAVA)資料庫型別Java
- Linux程式開發中如何判斷目錄是否為根目錄?Linux
- js根據IP地址判斷城市JS
- 判斷是否微信 IPhone 開啟iPhone
- 請問 httprunner 的 yaml file 是根據什麼格式判斷是否符合規格的?HTTPYAML
- 根據年月日判斷星期幾的c程式C程式
- Android判斷裝置是否開啟WIFI、GPRS資料連線AndroidWiFi
- 判斷excel檔案是否被開啟Excel
- 同步快速判斷視訊是否可以播放
- 判斷a是否是int型別資料型別
- PHP:判斷是否是JSON資料PHPJSON
- 根據開源資料庫選擇合適的工具資料庫
- iOS判斷使用者是否開啟APP通知開關iOSAPP
- js判斷字串是否是以指定的子字串開頭JS字串
- 你是否真的需要64位的JDK呢?你是根據什麼來判斷與決定呢?JDK
- 點選按鈕時根據select的值判斷是否是需要的選項並顯示div
- 根據登錄檔鍵值判斷本機EXCEL版本Excel
- 【python資料分析】判斷資料框是否為空Python
- c# winform 判斷資料夾是否存在,新建資料夾,判斷資料夾存不存在C#ORM
- 如何判斷自己是否是一名優秀開發人員?
- 根據上次輸入操作的時間設定離開狀態
- 判斷資料庫是否需要例項恢復資料庫
- 根據rowid查詢資料檔案號,資料塊號
- VBA判斷指定的資料夾或檔案是否存在
- 直播app開發,判斷使用者是否是首次登入平臺APP
- js根據字尾判斷檔案檔案型別的程式碼JS型別
- python如何判斷一列是否有資料Python
- 用scanf_s判斷輸入資料是否合法
- Sql Server中判斷表或者資料庫是否存在SQLServer資料庫
- JavaScript 判斷checkbox核取方塊是否選中JavaScript
- jQuery判斷checkbox核取方塊是否選中jQuery
- JavaScript判斷checkbox核取方塊是否選中JavaScript
- update 修改資料時,依賴或者說需要根據另一個值來進行判斷l
- 如何判斷一個字串是否為純數字的問題,當然也可以判斷一個字串是否為純字母字串
- 傳統的MVC開發模式和前後端分離開發模式MVC模式後端