1. 程式人生 > >HDFS HA架構以及源代碼引導

HDFS HA架構以及源代碼引導

decided return property 正常的 except 共享存儲 並運行 coo validate

HA體系架構

相關知識介紹

HDFS master/slave架構,HDFS節點分為NameNode節點和DataNode節點。

NameNode存有HDFS的元數據:主要由FSImage和EditLog組成。

FSImage保存有文件的文件夾、分塊ID、文件權限等,EditLog保存有對HDFS的操作記錄。

DataNode存放分塊的數據,並採用CRC循環校驗方式對本地的數據進行校驗,DataNode周期性向NameNode匯報本機的信息。

NameNode單點故障:HDFS僅僅有一個NameNode節點。當NameNode崩潰後,整個HDFS集群隨之崩潰。

HDFS HA:為了解決NameNode的單點故障。為NameNode保存一個熱備,這樣namenode共同擁有兩個:Active Namenode、Standby Namenode。集群使用的時候。用的是Active Namenode,而Standby Namenode存放的是Active Namenode的熱備。

Standby NN的功能

  1. 作為Active NN的熱備。當Active NN崩潰的時候。高速的切換成Active NN
  2. 充當曾經Secondary NN的角色:合並FSImage和EditLog。並將FSImage傳回給Active NN。Standby NN周期性監控共享存儲中EditLog的狀態變化。當監控到變化的時候,Standby NN會讀取該Log,並更新本機上的FSImage,之後再啟動一個線程,將該FSImage增量更新到Active NN上。

存儲共享:共享HDFS的操作日誌Editlog。能夠使用Quorum Journal Manager (QJM)或者NFS作為存儲共享模塊。

腦裂:集群中有兩個NN同一時候控制集群。當Active NN失效時,StandbyNN切換成Active NN,當原來的Active NN活過來時,集群中就有兩個Active NN了,這時就有兩個NN能夠控制集群。這就是腦裂。

HA手動模式架構

Active NN 和Standby NN之間通過JN共享EditLog。QJM負責向JN寫EditLog。HA架構例如以下所看到的。

技術分享

搭建過程參考http://blog.csdn.net/jiewuyou/article/details/21779247

搭建好後的效果例如以下:

技術分享

QJM/Qurom Journal Manager的架構如圖所看到的。

QJM 採用Paxos 算法 。大概思路是,有2N+1個節點作為JN ,當有N+1個JN更新成功時,就算更新成功。

QJM是一個輕量級的共享存儲。能夠和DN部署在一個節點上。Quorum JornalManager執行在Active NameNode上,用於管理JournalNode,並向JN更新EditLog。

技術分享

[1] Active NN向JN中更新EditLog的時候,是並行寫的,和HDFS中block的流式寫是有差別的

[2] Standby NN感知到EditLog中有更新時,會從JN中選擇一個存有該更新的JN,並讀取該更新

隔離(Fencing)

隔離(Fencing)是為了防止腦裂,就是保證在不論什麽時候HDFS僅僅有一個Active NN,主要包含三個方面:

? 共享存儲fencing,確保僅僅有一個NN能夠寫入edits。QJM中每個JournalNode中均有一個epochnumber,匹配epochnumber的QJM才有權限更新JN。當NN由standby狀態切換成active狀態時,會又一次生成一個epoch number。並更新JN中的epochnumber,以至於曾經的ActiveNN中的QJM中的epoch number和JN的epochnumber不匹配,故而原ActiveNN上的QJM沒法往JN中寫入數據(後面會介紹源代碼),即形成了fencing

? clientfencing,確保僅僅有一個NN能夠響應client的請求。

? DataNodefencing,確保僅僅有一個NN能夠向DN下發命令,譬如刪除塊。復制塊,等等。

QJM的Fencing方案僅僅能讓原來的Active NN失去對JN的寫權限,可是原來的Active NN還是能夠響應client的請求,對DN進行讀。配置dfs.ha.fencing.methods能夠指定Fencing的方法。

Hadoop公共庫中有兩種Fencing實現:sshfence、shell

sshfence:ssh到原Active NN上,結束進程(通過tcpport號定位進程pid。該方法比jps命令更準確)。

shell - run an arbitraryshell command to fencethe Active NameNode,即運行一個用戶事先定義的shell命令(腳本)完畢隔離。

你也能夠重寫org.apache.hadoop.ha.NodeFencer文件,生成自己的Fencing方法。

自己主動故障切換AutomaticFailover

自己主動切換架構

技術分享

來自:http://zh.hortonworks.com/blog/namenode-high-availability-in-hdp-2-0/

配置:http://hadoop.apache.org/docs/r2.3.0/hadoop-yarn/hadoop-yarn-site/HDFSHighAvailabilityWithQJM.html

Automated Failover 當active namenode崩潰的時候,自己主動將standby namenode切換成active namenode。

Hot Standby Namenode Standby NN維持著HDFS的元數據,能夠在Failover的時候高速的進行切換。實現原理:

1) DN向兩個NN同一時候發送心跳匯報

2) Standby NN會實時的讀取共享存儲中EditLog裏面的日誌

Full Stack Resiliency 在使用Failover的時候,HDP已經證實不會影響其上作業的執行。

ZooKeeper FailoverController (ZKFC)用於決定何時進行failover。共同擁有兩個ZKFC進程,分別執行在兩個NN上。

它會實時監控NN的狀態,一旦Active NN不能提供服務的時候,就進行failover。

源代碼

Qjournal

Qjournal包

org.apache.hadoop.hdfs.qjournal:這個包是測試JournalNode用的

MiniJournalCluster

QJMTestUtil

TestMiniJournalCluster

TestNNWithQJM

org.apache.hadoop.hdfs.qjournal.client:客戶端,提供對qjournal的相關操作

QuromJournalManager

執行在NameNode上,用來管理JNs,並向JNs更新EditLog。

QuorumOutputStream

實現接口EditLogOutputStream。用於向JN寫數據

SegmentRecoveryComparator

能夠比較各個JN的Log Segment。看哪個JN的質量更高,以選擇同步用的Log Segment源。

比如在NN切換成Active的時候。JN的Log Segment可能不一致,通過該類就能夠選擇Log Segment同步源。其它JN須要同步該Log Segment同步源

接口AsyncLogger

遠程異步通信接口

IPCLoggerChannel

AsyncLogger的實現。通過Hadoop IPC和JN遠程通信的管道

org.apache.hadoop.hdfs.qjournal.protocol:保存有QuorumJournalManager和JournalNode之間的通信協議接口

接口QJournalProtocol

QJM、JNs之間的通信協議,該協議用於發送EditLog,以及節點間的coordinating recovery

RequestInfo

請求信息

JournalOutOfSyncException

JournalNotFormattedException

Exception indicating that a call has been made to a JournalNode which is not yet formatted.

org.apache.hadoop.hdfs.qjournal.protocolPB

org.apache.hadoop.hdfs.qjournal.server:保存有qjournal相關服務

GetJournalEditServlet

This servlet is used in two cases:

· The QuorumJournalManager, when reading edits, fetches the edit streams from the journal nodes.

· During edits synchronization, one journal node will fetch edits from another journal node.

JNStorage

JN數據存儲的實現

Journal

JN能夠和不同的集群通信,這是通過Journal實現的。雖然這些Journal是全然獨立的。但他們執行在一個JVM裏面的

JournalMetrics

The server-side metrics for a journal from the JournalNode‘s perspective.

JournalNode

The JournalNode is a daemon which allows namenodes using the QuorumJournalManager to log and retrieve edits stored remotely. It is a thin wrapper around a local edit log directory with the addition of facilities to participate in the quorum protocol.

JournalNodeHttpServer

封裝有HTTP服務。由Journal服務啟動

JournalNodeRpcServer

JN上的RPC實現類

RPC

上面在代碼中提到了RPC,QJM的RPC主要就一個協議類:QuorumJournalManager與多個JournalNode通信的協議QJournalProtocol。那麽RPC的通信兩方的實體類各自是哪個呢?client(QuorumJournalManager)是QJournalProtocolTranslatorPB。server端(JournalNode)是JournalNodeRpcServer。

org.apache.hadoop.ha

技術分享

org.apache.hadoop.hdfs.server.namenode.ha

技術分享

過程分析

ActiveNN啟動過程

NN進入Active NN時,會運行ActiveState.enterState(),調用步驟例如以下,後面的一系列過程能夠參考StandbyNN切換成Active的過程

NameNode(Configuration conf, NamenodeRole role)

ActiveState.enterState()

NameNode.startActiveServices()

FSNamesystem.startActiveServices()

EditLog格式化

Actice NN 上的FSImage初始化完畢後。須要格式化EditLog。

FSNamesystem. loadFSImage()

FSImage.format()

FSEditLog. formatNonFileJournals ()

QuorumJournalManager.format(NamespaceInfo nsInfo)

相對於Paxos 算法,format操作是比較特殊的。要求全部的JN返回都成功時才行,由於它相當於是做了個初始化的工作。

而在後面更新數據的過程中,僅僅要大多數success response就覺得這次寫成功了。

Automatic Failover過程

技術分享

共兩個ZKFC,分別執行在兩個NN上,同一時候ZookeeprService維持有Active NN的鎖。Active NN上的ZKFC會監控該NN的狀態並管理HA狀態,一旦Actice NN失效的時候,ZKFC會從Zookeeper Service上釋放Active NN鎖。

Standby NN上的ZKFC也會監控該NN的狀態,並嘗試從Zookeeper Service上獲取Active NN的鎖。當Active NN失去該鎖的時候。StandbyNN上的ZKFC會接管該鎖,並將 Standby NN的狀態切換成Active NN。

相關源代碼

package org.apache.hadoop.ha

HealthMonitor.java

ZKFailoverController.java

接口ZKFCProtocol.java

ZKFCRpcServer.java

1.監控NN狀態

調用過程:

ZKFailoverCtroller.run()

ZKFailoverCtroller. doRun()

ZKFailoverController.initHM()

HealthMonitor.start();

MonitorDaemon.start()

MonitorDaemon.run();

分析MonitorDaemon.run()

public void run() {
  while (shouldRun) {
    try {
      //等待HAServiceProtocol可用
      loopUntilConnected();
      //監控服務狀態。並進行對應處理
      doHealthChecks();
    } catch (InterruptedException ie) {
      Preconditions.checkState(!shouldRun,
          "Interrupted but still supposed to run");
    }
  }
}

doHealthChecks()經過一系列的調用後,會調用NameNode.monitorHealth(),用於監控NameNode可用狀態。

當NN沒有資源可用時,拋出異常。

2. 監控到服務不可用時

上面提到,當服務不可用的時候,會拋出異常。

監測到異常State.SERVICE_UNHEALTHY時

HealthMonitor.doHealthChecks()

enterState(State.SERVICE_UNHEALTHY);

監測到異常 State.SERVICE_NOT_RESPONDING)時

HealthMonitor.doHealthChecks()

enterState(State. SERVICE_HEALTHY);

在enterState()裏面,會經過一系列回調函數

HealthMonitor.enterState()

HealthCallbacks. enteredState();

ZKFailoverController.recheckElectability()

ActiveStandbyElector. quitElection(true);

ActiveStandbyElector. tryDeleteOwnBreadCrumbNode()

之後,Active NN上的ZKFC會失去ZookeeperService上的Active NN鎖。而Standby NN上的ZKFC一直在嘗試獲取該鎖。此時。Standby NN上的ZKFC就獲得了該鎖,當Standby NN上的ZKFC獲取Active NN鎖的時候。會將NN切換成Actice。

Standby切換成Actice過程

參考:http://yanbohappy.sinaapp.com/?p=205

函數調用過程

NameNode.setStateInternal(final HAContext context, final HAState s)//狀態轉換

ActiveState. enterState()

接下來就該看看一個StandbyNN由Standby變成Active時,須要運行哪些操作:

1) fencing原來Active NN的寫。

2) recover in-progress logs。

原來Active NN寫EditLog過程中發生了主從切換,那麽處在不同JournalNode上的EditLog的數據可能不一致。須要把不同JournalNode上的EditLog同步一致,而且finalized。(這個過程類似於HDFS append中的recover lease的過程)

3) startLogSegment。讓切換成Active的NN擁有寫日誌功能。

1. fencing原來Active NN的寫

基於QJM的HA不須要處理fencing問題。

這是怎麽做到的呢?解決問題靠的是epoch number,這個和Paxos算法中選主(master election)所做的工作類似。QJM和JN均保存有一個唯一的epoch number,僅僅有擁有這個epoch number的NameNode才幹夠往Journal Node寫數據。

系統初始化、或者Standby NameNode切換成Active Namenode時,都會運行QourumJournalManager.recoverUnfinalizedSegments()。在生成新的epochnumber後QourumJournalManager會通過RPC將該epochnumber發送給各個JournalNode。

一系列的“擦屁股”的操作結束之後。當原來的Active NameNode想寫日誌時。由於epoch number沒法匹配journal node的epoch number。這樣寫操作被拒絕。

當Active 和Standby NN 發生主從切換時,原來的StandbyNN須要運行:

NameNode.setStateInternal(final HAContext context, final HAState s)//狀態轉換

ActiveState. enterState()

NameNode.startActiveServices()

FSNamesystem.startActiveServices()

FSEditLog.recoverUnclosedStreams()

JournalSet.recoverUnfinalizedSegments()

QourumJournalManager.recoverUnfinalizedSegment()

這個過程說白了就是給原來的ActiveNN擦屁股。也能夠算作是Standby要接管qjournal寫權利的開始。這裏面就出現了我們所說的brain-split的問題。Standby NN怎麽保證原來的Active NN已經不再往qjournal上寫數據了。看看QourumJournalManager.recoverUnfinalizedSegment()的實現過程:

// Fence any previous writers, and obtain a unique epoch number for write-access to the journal nodes.Returns:the new, unique epoch number
public void recoverUnfinalizedSegments() throws IOException {
    Preconditions.checkState(!isActiveWriter, "already active writer");
    LOG.info("Starting recovery process for unclosed journal segments...");
    //這句話攻克了brain-split問題。也就是fencing writer
    Map<AsyncLogger, NewEpochResponseProto> resps = createNewUniqueEpoch();
    LOG.info("Successfully started new epoch " + loggers.getEpoch());
    if (LOG.isDebugEnabled()) {
      LOG.debug("newEpoch(" + loggers.getEpoch() + ") responses:\n" +
        QuorumCall.mapToString(resps));
    }
    //找出最後一塊edit log segment,由於僅僅有最後一塊有可能是不完整的。

long mostRecentSegmentTxId = Long.MIN_VALUE; for (NewEpochResponseProto r : resps.values()) { if (r.hasLastSegmentTxId()) { mostRecentSegmentTxId = Math.max(mostRecentSegmentTxId, r.getLastSegmentTxId()); } } // On a completely fresh system, none of the journals have any // segments, so there‘s nothing to recover. if (mostRecentSegmentTxId != Long.MIN_VALUE) { //把不完整的log segment恢復完整 recoverUnclosedSegment(mostRecentSegmentTxId); } isActiveWriter = true; }

Epoch攻克了我們所說的問題。StandbyNN向每一個JournalNode發送getJournalState RPC請求。JN返回自己的lastPromisedEpoch。

QuorumJournalManager收到大多數JN返回的lastPromisedEpoch,在當中選擇最大的一個,然後加1作為當前QJM的epoch,同一時候通過發送newEpoch RPC把這個新的epoch寫到qjournal上。由於在這之後每次QuorumJournalManager在向qjournal運行寫相關操作(startLogSegment(),logEdits(), finalizedLogSegment()等)的時候。都要把自己的epoch作為參數傳遞過去。寫相關操作到達每一個JournalNode端會比較假設傳過來的epoch假設小於JournalNode端存儲的lastPromisedEpoch,那麽這次寫相關操作會被拒絕。假設大多數JournalNode都拒絕了這次寫相關操作,這次操作就失敗了。回到我們眼下的邏輯中,在主從切換時。原來的Standby NN把epoch+1了之後,原來的Active NN的epoch就肯定比這個小了。那麽假設它再向qjournal寫日誌就會被拒絕。

由於qjournal不接收比lastPromisedEpoch小的QJM寫日誌。

看看JN收到newEpoch RPC之後怎麽辦:JN檢查來自QJM的這個epoch和自己存儲的lastPromisedEpoch:假設來自writer的epoch小於lastPromisedEpoch,那麽說明不同意這個writer向JNs寫數據了,拋出異常,writer端收到異常response。那麽達不到大多數的successresponse,就不會有寫qjournal的權限了。(事實上這個過程就是Paxos算法裏面選主的過程)。

2. recover in-progress logs

接著上面的代碼。Standby已經通過createNewUniqueEpoch()來fencing原來的Active。這個RPC請求除了會返回epoch。還會返回最後一個logsegment的txid。由於僅僅有最後一個log segment可能須要恢復

這個recover算法就是Paxos算法的一個實例(instance),目的是使得分布在不同JN上的log segment的數據達成一致。

接下來就開始recoverUnclosedSegment()恢復算法。

private void recoverUnclosedSegment(long segmentTxId) throws IOException {
    Preconditions.checkArgument(segmentTxId > 0);
    LOG.info("Beginning recovery of unclosed segment starting at txid " +
        segmentTxId);
    // Step 1. Prepare recovery
    //QJM向JNs問segmentTxId相應的segment的長度和finalized/in-progress狀況;JNs返回這些信息。(相應Paxos算法的Phase 1a和Phase 1b)
    QuorumCall<AsyncLogger,PrepareRecoveryResponseProto> prepare =
        loggers.prepareRecovery(segmentTxId);
    Map<AsyncLogger, PrepareRecoveryResponseProto> prepareResponses=
        loggers.waitForWriteQuorum(prepare, prepareRecoveryTimeoutMs,
            "prepareRecovery(" + segmentTxId + ")");
    LOG.info("Recovery prepare phase complete. Responses:\n" +
        QuorumCall.mapToString(prepareResponses));
    //在每一個JN的返回信息中通過SegmentRecoveryComparator比較。選擇當中最好的一個log segment作為後面同步log的標準。

//怎樣選擇更好的Log segment後面有詳解。

Entry<AsyncLogger, PrepareRecoveryResponseProto> bestEntry = Collections.max( prepareResponses.entrySet(), SegmentRecoveryComparator.INSTANCE); AsyncLogger bestLogger = bestEntry.getKey(); PrepareRecoveryResponseProto bestResponse = bestEntry.getValue(); // Log the above decision, check invariants. if (bestResponse.hasAcceptedInEpoch()) { LOG.info("Using already-accepted recovery for segment " + "starting at txid " + segmentTxId + ": " + bestEntry); } else if (bestResponse.hasSegmentState()) { LOG.info("Using longest log: " + bestEntry); } else { //prepareRecovery RPC沒有返回不論什麽指定txid的segment,原因可能例如以下: //有3個JNs: JN1,JN2,JN3。

原來的Active NN 在JN1上開始寫segment 101, //然後原來Active NN掛了。主從切換,此時segment 101在JN2和JN3上並不存在。 //newEpoch RPC,由於我們看到了JN1上的segment 101。所以決定recover的是segment 101 //在prepareRecovery之前,JN1掛了。那麽prepareRecovery RPC僅僅能發向JN2和JN3了,RPC返回的結果是沒有segment 101 //這樣的情況下是不須要recover的,由於segment 101並沒有寫成功(沒有達到大多數) for (PrepareRecoveryResponseProto resp : prepareResponses.values()) { assert !resp.hasSegmentState() : "One of the loggers had a response, but no best logger " + "was found."; } LOG.info("None of the responders had a log to recover: " + QuorumCall.mapToString(prepareResponses)); return; } SegmentStateProto logToSync = bestResponse.getSegmentState(); assert segmentTxId == logToSync.getStartTxId(); // Sanity check: none of the loggers should be aware of a higher // txid than the txid we intend to truncate to for (Map.Entry<AsyncLogger, PrepareRecoveryResponseProto> e : prepareResponses.entrySet()) { AsyncLogger logger = e.getKey(); PrepareRecoveryResponseProto resp = e.getValue(); if (resp.hasLastCommittedTxId() && resp.getLastCommittedTxId() > logToSync.getEndTxId()) { throw new AssertionError("Decided to synchronize log to " + logToSync + " but logger " + logger + " had seen txid " + resp.getLastCommittedTxId() + " committed"); } } //同步log的數據源JN找到後,構造URL用於其它JN讀取EditLog(JN端有HTTP server通過servlet形式提供HTTP讀) URL syncFromUrl = bestLogger.buildURLToFetchLogs(segmentTxId); //向JNs發送acceptRecovery RPC請求(相應Paxos算法的Phase 2a) //JN收到這個acceptRecovery RPC之後,使自己的log與syncFromUrl同步。並持久化這個logsegment和epoch //假設收到大多數的JNs的success response,那麽這個同步操作成功。(相應Paxos算法的Phase 2b) QuorumCall<AsyncLogger,Void> accept = loggers.acceptRecovery(logToSync, syncFromUrl); loggers.waitForWriteQuorum(accept, acceptRecoveryTimeoutMs, "acceptRecovery(" + TextFormat.shortDebugString(logToSync) + ")"); // If one of the loggers above missed the synchronization step above, but // we send a finalize() here, that‘s OK. It validates the log before // finalizing. Hence, even if it is not "in sync", it won‘t incorrectly // finalize. //EditLog既然已經同步完了。那麽就應該正常finalized了。

QuorumCall<AsyncLogger, Void> finalize = loggers.finalizeLogSegment(logToSync.getStartTxId(), logToSync.getEndTxId()); loggers.waitForWriteQuorum(finalize, finalizeSegmentTimeoutMs, String.format("finalizeLogSegment(%s-%s)", logToSync.getStartTxId(), logToSync.getEndTxId())); }


代碼中留給我們一個問題,就是什麽樣的log segment是更好的,在recover的過程中被選為同步源呢。具體的設計能夠參考Todd寫的<<Quorum-Journal Design>>https://issues.apache.org/jira/secure/attachment/12547598/qjournal-design.pdf 的2.9和2.10。在代碼中的實現是SegmentRecoveryComparator類。

簡單描寫敘述下原理就是:有finalized的不用in-progress的;假設有多個finalized必須length一致。沒有finalized的看誰的epoch更大;假設前面的都一樣就看誰的最後一個txid更大。

在<<Quorum-Journal Design>>中有詳細的樣例。我看完這塊之後感覺和HDFS append的block recover過程中選擇同步源的思路有異曲同工之妙。

經歷了上面的QourumJournalManager.recoverUnfinalizedSegment()過程。不完整的logsegment都是完整的了,接下來就是調用EditLogTailer.doTailEdits()。原來Standby NN先去和原來ActiveNN同步EditLog,然後把EditLog運行,這時兩臺NN內存數據才真正一致。

這裏會調用QuorumJournalManager.selectInputStreams()從JNs中讀取 EditLog。

並且眼下HDFS中僅僅有finalizededit log才幹被Standby NN讀取並運行。在Standby NN從JNs讀取EditLog時,首先向全部的JN節點發送getEditLogManifest() RPC去讀取大於某一txid而且已經finalizededit log segment。收到大多數返回success,則把這些log segment整合成一個RedundantEditLogInputStream,然後Standby NN僅僅要向當中的一臺JN讀取數據即可了。

至此原來的Standby NN所做的擦屁股的工作就結束了。那麽它就正式變成了Active NN,接下來就是正常的記錄日誌的工作了。

3. startLogSegment

也是初始化QuorumOutputStream的過程。

NameNode.startActiveServices()

FSNamesystem.startActiveServices()

FSEditLog.openForWrite()

FSEditLog.startLogSegmentAndWriteHeaderTxn()

FSEditLog.startLogSegment()

JournalSet.startLogSegment()//返回值是QuorumOutputStream

JournalSet.startLogSegment()

QuorumJournalManager.startLogSegment()

QJM向JNs發送startLogSegmentRPC調用,假設收到多數success response則成功。用這個AsynaLogSet構造QuorumOutputStream用於寫log。

Active NN更新EditLog過程

1. 初始化QuorumOutputStream

在ActiveState.enterState()階段已經完畢,參考3.4.3

2. 更新EditLog

通過以下的調用把Log寫到QuorumOutputStream的doublebuffer裏面。

由QuorumOutputStream實現更新。

org.apache.hadoop.hdfs.server.namenode.FSEditLog.logEdit()

org.apache.hadoop.hdfs.qjournal.client.QuorumOutputStream.write()

3. 同步Log

FSEditLog.logEdit()

FSEditLog.logSync()

EditLogOutputStream.flush()

QuorumOutputStream.flushAndSync()

flushAndSync()通過AsyncLoggerSet.sendEdits()調用Journal RPC把相應的日誌寫到JNs,相同是大多數successresponse即覺得成功。假設大多數返回失敗的話,這次logSync操作失敗。那麽NameNode會abort,由於沒法正常寫日誌了。

client選擇ActiceNN

實現類 org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider

功能 幫助Client選擇哪個節點是主節點

說明 A FailoverProxyProvider implementation which allows one to configuretwo URIs to connect to during fail-over. The first configured address is triedfirst, and on a fail-over event the other address is tried.

算法1. getActiveNN()

輸入nn1、nn2

輸出ActiveNN

開始

1. ActiveNN=null

2. IF isConnective(nn1) andisActive(nn1)THEN

3. ActiveNN=nn1

4. ELSE

5. IF isConnective(nn2) andisActive(nn2)THEN

6. ActiveNN=nn2

7. END IF

8. END ELSE

9. END IF

結束

配置:

<property>

<name>dfs.client.failover.proxy.provider.mycluster</name>

<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>

</property>

Standby NN啟動時同步Active NN元數據的過程

Active NN啟動後,Standby NN能夠通過這兩個腳本啟動

bin/hdfs namenode -bootstrapStandby

sbin/hadoop-daemon.sh start namenode

第一個腳本用於初始化StandbyNN,其功能例如以下:

[1] 和nn1通信,獲取namespace metadata和checkpointedfsimage;

[2] 從JN中獲取EditLog

可是腳本會在下列情況下失效:JN沒有初始化成功。不能提供EditLog。

相關的實現類:

org.apache.hadoop.hdfs.server.namenode.ha.BootstrapStandby

調用過程

NameNode. createNameNode()

BootstrapStandby.run(toolArgs, conf)

ToolRunner.run(BootstrapStandby, argv);

BootstrapStandby.run()

BootstrapStandby .doRun()//該函數負責bootstrapStandby過程

TransferFsImage.downloadImageToStorage();//下載FSImage

註意:FSImage中封裝了EditLog。HA中EditLog的存儲空間在JN中。

Standby NN更新

實現類:org.apache.hadoop.hdfs.server.namenode.ha.StandbyCheckpointer

說明:Threadwhich runs inside the NN when it‘s in Standby state, periodically waking up totake a checkpoint of the namespace. When it takes a checkpoint, it saves it toits local storage and then uploads it to the remote NameNode.

該類裏面封裝了線程CheckpointerThread

CheckpointerThread.run()

CheckpointerThread.doWork()

CheckpointerThread.doCheckpoint()//檢測是否須要進行更新

當檢測到更新的時候,會將EditLog更新下載到本地同一時候進行合並成FSImage,並將最新的FSImage增量更新到Active NN上。

CheckpointerThread.doCheckpoint()//檢測是否須要進行更新

TransferFsImage.uploadImageFromStorage( )

activeNNAddress, myNNAddress,namesystem.getFSImage().getStorage(), txid);

參考資料

[1] HDFS High Availability Using the Quorum JournalManager

[2] HDFS 體系結構

[3] Hadoop 2.0 NameNode HA和Federation實踐

[4] 基於QJM/Qurom Journal Manager/Paxos的HDFS HA原理及代碼分析

[5] Hadoop 2.0中單點故障解決方式總結

[6] Paxos算法

HDFS HA架構以及源代碼引導