Hadoop技術之Hadoop HA 機制學習

歡迎你們前往騰訊雲技術社區,獲取更多騰訊海量技術實踐乾貨哦~node

做者:溫球良 緩存

導語

最近分享過一次關於Hadoop技術主題的演講,因爲接觸時間不長,不少技術細節認識不夠,也沒講清楚,做爲一個技術人員,本着追根溯源的精神,仍是有必要吃透,也爲本身的工做沉澱一些經驗總結。網上關於Hadoop HA的資料多集中於怎麼搭建HA,對於HA爲何要這麼作描述甚少,因此本文對於HA是如何搭建的暫不介紹,主要是介紹HA是怎麼運做,QJM又是怎麼發揮功效的。安全

1、Hadoop 系統架構

1.1 Hadoop1.x和Hadoop2.x 架構

在介紹HA以前,咱們先來看下Hadoop的系統架構,這對於理解HA是相當重要的。Hadoop 1.x以前,其官方架構如圖1所示:session

[ 圖1.Hadoop 1.x架構圖 ]架構

從圖中可看出,1.x版本以前只有一個Namenode,全部元數據由唯一的Namenode負責管理,可想而之當這個NameNode掛掉時整個集羣基本也就不可用。
Hadoop 2.x的架構與1.x有什麼區別呢。咱們來看下2.x的架構:框架

[ 圖2.Hadoop 2.x架構圖 ]異步

2.x版本中,HDFS架構解決了單點故障問題,即引入雙NameNode架構,同時藉助共享存儲系統來進行元數據的同步,共享存儲系統類型通常有幾類,如:Shared NAS+NFS、BookKeeper、BackupNode 和 Quorum Journal Manager(QJM),上圖中用的是QJM做爲共享存儲組件,經過搭建奇數結點的JournalNode實現主備NameNode元數據操做信息同步。Hadoop的元數據包括哪些信息呢,下面介紹下關於元數據方面的知識。分佈式

1.2 Hadoop 2.x元數據

Hadoop的元數據主要做用是維護HDFS文件系統中文件和目錄相關信息。元數據的存儲形式主要有3類:內存鏡像、磁盤鏡像(FSImage)、日誌(EditLog)。在Namenode啓動時,會加載磁盤鏡像到內存中以進行元數據的管理,存儲在NameNode內存;磁盤鏡像是某一時刻HDFS的元數據信息的快照,包含全部相關Datanode節點文件塊映射關係和命名空間(Namespace)信息,存儲在NameNode本地文件系統;日誌文件記錄client發起的每一次操做信息,即保存全部對文件系統的修改操做,用於按期和磁盤鏡像合併成最新鏡像,保證NameNode元數據信息的完整,存儲在NameNode本地和共享存儲系統(QJM)中。ide

以下所示爲NameNode本地的EditLog和FSImage文件格式,EditLog文件有兩種狀態: inprocess和finalized, inprocess表示正在寫的日誌文件,文件名形式:editsinprocess[start-txid],finalized表示已經寫完的日誌文件,文件名形式:edits[start-txid][end-txid]; FSImage文件也有兩種狀態, finalized和checkpoint, finalized表示已經持久化磁盤的文件,文件名形式: fsimage_[end-txid], checkpoint表示合併中的fsimage, 2.x版本checkpoint過程在Standby Namenode(SNN)上進行,SNN會按期將本地FSImage和從QJM上拉回的ANN的EditLog進行合併,合併完後再經過RPC傳回ANN。oop

data/hbase/runtime/namespace ├── current │ ├── VERSION │ ├── edits_0000000003619794209-0000000003619813881 │ ├── edits_0000000003619813882-0000000003619831665 │ ├── edits_0000000003619831666-0000000003619852153 │ ├── edits_0000000003619852154-0000000003619871027 │ ├── edits_0000000003619871028-0000000003619880765 │ ├── edits_0000000003619880766-0000000003620060869 │ ├── edits_inprogress_0000000003620060870 │ ├── fsimage_0000000003618370058 │ ├── fsimage_0000000003618370058.md5 │ ├── fsimage_0000000003620060869 │ ├── fsimage_0000000003620060869.md5 │ └── seen_txid └── in_use.lock 

上面所示的還有一個很重要的文件就是seen_txid,保存的是一個事務ID,這個事務ID是EditLog最新的一個結束事務id,當NameNode重啓時,會順序遍歷從edits_0000000000000000001到seen_txid所記錄的txid所在的日誌文件,進行元數據恢復,若是該文件丟失或記錄的事務ID有問題,會形成數據塊信息的丟失。

HA其本質上就是要保證主備NN元數據是保持一致的,即保證fsimage和editlog在備NN上也是完整的。元數據的同步很大程度取決於EditLog的同步,而這步驟的關鍵就是共享文件系統,下面開始介紹一下關於QJM共享存儲機制。

2、QJM原理

2.1 QJM背景

在QJM出現以前,爲保障集羣的HA,設計的是一種基於NAS的共享存儲機制,即主備NameNode間經過NAS進行元數據的同步。該方案有什麼缺點呢,主要有如下幾點:

  • 定製化硬件設備:必須是支持NAS的設備才能知足需求
  • 複雜化部署過程:在部署好NameNode後,還必須額外配置NFS掛載、定製隔離腳本,部署易出錯
  • 簡陋化NFS客戶端:Bug多,部署配置易出錯,致使HA不可用

因此對於替代方案而言,也必須解決NAS相關缺陷才能讓HA更好服務。即設備無須定製化,普通設備便可配置HA,部署簡單,相關配置集成到系統自己,無需本身定製,同時元數據的同步也必須保證徹底HA,不會因client問題而同步失敗。

2.2 QJM原理

2.2.1 QJM介紹

QJM全稱是Quorum Journal Manager, 由JournalNode(JN)組成,通常是奇數點結點組成。每一個JournalNode對外有一個簡易的RPC接口,以供NameNode讀寫EditLog到JN本地磁盤。當寫EditLog時,NameNode會同時向全部JournalNode並行寫文件,只要有N/2+1結點寫成功則認爲這次寫操做成功,遵循Paxos協議。其內部實現框架以下:

[ 圖3.QJM內部實現框架 ]

從圖中可看出,主要是涉及EditLog的不一樣管理對象和輸出流對象,每種對象發揮着各自不一樣做用:

  • FSEditLog:全部EditLog操做的入口
  • JournalSet: 集成本地磁盤和JournalNode集羣上EditLog的相關操做
  • FileJournalManager: 實現本地磁盤上 EditLog 操做
  • QuorumJournalManager: 實現JournalNode 集羣EditLog操做
  • AsyncLoggerSet: 實現JournalNode 集羣 EditLog 的寫操做集合
  • AsyncLogger:發起RPC請求到JN,執行具體的日誌同步功能
  • JournalNodeRpcServer:運行在 JournalNode 節點進程中的 RPC 服務,接收 NameNode 端的 AsyncLogger 的 RPC 請求。
  • JournalNodeHttpServer:運行在 JournalNode 節點進程中的 Http 服務,用於接收處於 Standby 狀態的 NameNode 和其它 JournalNode 的同步 EditLog 文件流的請求。

下面具體分析下QJM的讀寫過程。

2.2.2 QJM 寫過程分析

上面提到EditLog,NameNode會把EditLog同時寫到本地和JournalNode。寫本地由配置中參數dfs.namenode.name.dir控制,寫JN由參數dfs.namenode.shared.edits.dir控制,在寫EditLog時會由兩個不一樣的輸出流來控制日誌的寫過程,分別爲:EditLogFileOutputStream(本地輸出流)和QuorumOutputStream(JN輸出流)。寫EditLog也不是直接寫到磁盤中,爲保證高吞吐,NameNode會分別爲EditLogFileOutputStream和QuorumOutputStream定義兩個同等大小的Buffer,大小大概是512KB,一個寫Buffer(buffCurrent),一個同步Buffer(buffReady),這樣能夠一邊寫一邊同步,因此EditLog是一個異步寫過程,同時也是一個批量同步的過程,避免每寫一筆就同步一第二天志。

這個是怎麼實現邊寫邊同步的呢,這中間實際上是有一個緩衝區交換的過程,即bufferCurrent和buffReady在達到條件時會觸發交換,如bufferCurrent在達到閾值同時bufferReady的數據又同步完時,bufferReady數據會清空,同時會將bufferCurrent指針指向bufferReady以知足繼續寫,另外會將bufferReady指針指向bufferCurrent以提供繼續同步EditLog。上面過程用流程圖就是表示以下:

[ 圖4.EditLog輸出流程圖 ]

這裏有一個問題,既然EditLog是異步寫的,怎麼保證緩存中的數據不丟呢,其實這裏雖然是異步,但實際全部日誌都須要經過logSync同步成功後纔會給client返回成功碼,假設某一時刻NameNode不可用了,其內存中的數據實際上是未同步成功的,因此client會認爲這部分數據未寫成功。

第二個問題是,EditLog怎麼在多個JN上保持一致的呢。下面展開介紹。

1.隔離雙寫:

在ANN每次同步EditLog到JN時,先要保證不會有兩個NN同時向JN同步日誌。這個隔離是怎麼作的。這裏面涉及一個很重要的概念Epoch Numbers,不少分佈式系統都會用到。Epoch有以下幾個特性:

  • 當NN成爲活動結點時,其會被賦予一個EpochNumber

  • 每一個EpochNumber是唯一的,不會有相同的EpochNumber出現

  • EpochNumber有嚴格順序保證,每次NN切換後其EpochNumber都會自增1,後面生成的EpochNumber都會大於前面的EpochNumber

QJM是怎麼保證上面特性的呢,主要有如下幾點:

  • 第一步,在對EditLog做任何修改前,QuorumJournalManager(NameNode上)必須被賦予一個EpochNumber
  • 第二步, QJM把本身的EpochNumber經過newEpoch(N)的方式發送給全部JN結點
  • 第三步, 當JN收到newEpoch請求後,會把QJM的EpochNumber保存到一個lastPromisedEpoch變量中並持久化到本地磁盤
  • 第四步, ANN同步日誌到JN的任何RPC請求(如logEdits(),startLogSegment()等),都必須包含ANN的EpochNumber
  • 第五步,JN在收到RPC請求後,會將之與lastPromisedEpoch對比,若是請求的EpochNumber小於lastPromisedEpoch,將會拒絕同步請求,反之,會接受同步請求並將請求的EpochNumber保存在lastPromisedEpoch

這樣就能保證主備NN發生切換時,就算同時向JN同步日誌,也能保證日誌不會寫亂,由於發生切換後,原ANN的EpochNumber確定是小於新ANN的EpochNumber,因此原ANN向JN的發起的全部同步請求都會拒絕,實現隔離功能,防止了腦裂。

2. 恢復in-process日誌

爲何要這步呢,若是在寫過程當中寫失敗了,可能各個JN上的EditLog的長度都不同,須要在開始寫以前將不一致的部分恢復。恢復機制以下:

1 ANN先向全部JN發送getJournalState請求;
2 JN會向ANN返回一個Epoch(lastPromisedEpoch);
3 ANN收到大多數JN的Epoch後,選擇最大的一個並加1做爲當前新的Epoch,而後向JN發送新的newEpoch請求,把新的Epoch下發給JN;
4 JN收到新的Epoch後,和lastPromisedEpoch對比,若更大則更新到本地並返回給ANN本身本地一個最新EditLogSegment起始事務Id,若小則返回NN錯誤;
5 ANN收到多數JN成功響應後認爲Epoch生成成功,開始準備日誌恢復;
6 ANN會選擇一個最大的EditLogSegment事務ID做爲恢復依據,而後向JN發送prepareRecovery; RPC請求,對應Paxos協議2p階段的Phase1a,若多數JN響應prepareRecovery成功,則可認爲Phase1a階段成功;
7 ANN選擇進行同步的數據源,向JN發送acceptRecovery RPC請求,並將數據源做爲參數傳給JN。
8 JN收到acceptRecovery請求後,會從JournalNodeHttpServer下載EditLogSegment並替換到本地保存的EditLogSegment,對應Paxos協議2p階段的Phase1b,完成後返回ANN請求成功狀態。
9 ANN收到多數JN的響應成功請求後,向JN發送finalizeLogSegment請求,表示數據恢復完成,這樣以後全部JN上的日誌就能保持一致。
數據恢復後,ANN上會將本地處於in-process狀態的日誌改名爲finalized狀態的日誌,形式如edits[start-txid][stop-txid]。

3.日誌同步

這個步驟上面有介紹到關於日誌從ANN同步到JN的過程,具體以下:

1 執行logSync過程,將ANN上的日誌數據放到緩存隊列中
2 將緩存中數據同步到JN,JN有相應線程來處理logEdits請求
3 JN收到數據後,先確認EpochNumber是否合法,再驗證日誌事務ID是否正常,將日誌刷到磁盤,返回ANN成功碼
4 ANN收到JN成功請求後返回client寫成功標識,若失敗則拋出異常

經過上面一些步驟,日誌能保證成功同步到JN,同時保證JN日誌的一致性,進而備NN上同步日誌時也能保證數據是完整和一致的。

2.2.3 QJM讀過程分析

這個讀過程是面向備NN(SNN)的,SNN按期檢查JournalNode上EditLog的變化,而後將EditLog拉回本地。SNN上有一個線程StandbyCheckpointer,會按期將SNN上FSImage和EditLog合併,並將合併完的FSImage文件傳回主NN(ANN)上,就是所說的Checkpointing過程。下面咱們來看下Checkpointing是怎麼進行的。

在2.x版本中,已經將原來的由SecondaryNameNode主導的Checkpointing替換成由SNN主導的Checkpointing。下面是一個CheckPoint的流向圖:

[ 圖5.Checkpointing流向圖 ]

總的來講,就是在SNN上先檢查前置條件,前置條件包括兩個方面:距離上次Checkpointing的時間間隔和EditLog中事務條數限制。前置條件任何一個知足都會觸發Checkpointing,而後SNN會將最新的NameSpace數據即SNN內存中當前狀態的元數據保存到一個臨時的fsimage文件( fsimage.ckpt)而後比對從JN上拉到的最新EditLog的事務ID,將fsimage.ckpt_中沒有,EditLog中有的全部元數據修改記錄合併一塊兒並重命名成新的fsimage文件,同時生成一個md5文件。將最新的fsimage再經過HTTP請求傳回ANN。經過按期合併fsimage有什麼好處呢,主要有如下幾個方面:

  • 能夠避免EditLog愈來愈大,合併成新fsimage後能夠將老的EditLog刪除
  • 能夠避免主NN(ANN)壓力過大,合併是在SNN上進行的
  • 能夠保證fsimage保存的是一份最新的元數據,故障恢復時避免數據丟失

3、主備切換機制

要完成HA,除了元數據同步外,還得有一個完備的主備切換機制,Hadoop的主備選舉依賴於ZooKeeper。下面是主備切換的狀態圖:

[ 圖6.Failover流程圖 ]

從圖中能夠看出,整個切換過程是由ZKFC來控制的,具體又可分爲HealthMonitor、ZKFailoverController和ActiveStandbyElector三個組件。

  • ZKFailoverController: 是HealthMontior和ActiveStandbyElector的母體,執行具體的切換操做
  • HealthMonitor: 監控NameNode健康狀態,若狀態異常會觸發回調ZKFailoverController進行自動主備切換
  • ActiveStandbyElector: 通知ZK執行主備選舉,若ZK完成變動,會回調ZKFailoverController相應方法進行主備狀態切換

在故障切換期間,ZooKeeper主要是發揮什麼做用呢,有如下幾點:

  • 失敗保護:集羣中每個NameNode都會在ZooKeeper維護一個持久的session,機器一旦掛掉,session就會過時,故障遷移就會觸發
  • Active NameNode選擇:ZooKeeper有一個選擇ActiveNN的機制,一旦現有的ANN宕機,其餘NameNode能夠向ZooKeeper申請排他成爲下一個Active節點
  • 防腦裂: ZK自己是強一致和高可用的,能夠用它來保證同一時刻只有一個活動節點

那在哪些場景會觸發自動切換呢,從HDFS-2185中概括瞭如下幾個場景:

  • ActiveNN JVM奔潰:ANN上HealthMonitor狀態上報會有鏈接超時異常,HealthMonitor會觸發狀態遷移至SERVICE_NOT_RESPONDING, 而後ANN上的ZKFC會退出選舉,SNN上的ZKFC會得到Active Lock, 做相應隔離後成爲Active結點。
  • ActiveNN JVM凍結:這個是JVM沒奔潰,但也沒法響應,同奔潰同樣,會觸發自動切換。
  • ActiveNN 機器宕機:此時ActiveStandbyElector會失去同ZK的心跳,會話超時,SNN上的ZKFC會通知ZK刪除ANN的活動鎖,做相應隔離後完成主備切換。
  • ActiveNN 健康狀態異常: 此時HealthMonitor會收到一個HealthCheckFailedException,並觸發自動切換。
  • Active ZKFC奔潰:雖然ZKFC是一個獨立的進程,但因設計簡單也容易出問題,一旦ZKFC進程掛掉,雖然此時NameNode是OK的,但系統也認爲須要切換,此時SNN會發一個請求到ANN要求ANN放棄主結點位置,ANN收到請求後,會觸發完成自動切換。
  • ZooKeeper奔潰:若是ZK奔潰了,主備NN上的ZKFC都會感知斷連,此時主備NN會進入一個NeutralMode模式,同時不改變主備NN的狀態,繼續發揮做用,只不過此時,若是ANN也故障了,那集羣沒法發揮Failover, 也就不可用了,因此對於此種場景,ZK通常是不容許掛掉到多臺,至少要有N/2+1臺保持服務纔算是安全的。

5、總結

上面介紹了下關於HadoopHA機制,概括起來主要是兩塊:元數據同步和主備選舉。元數據同步依賴於QJM共享存儲,主備選舉依賴於ZKFC和Zookeeper。整個過程仍是比較複雜的,若是能理解Paxos協議,那也能更好的理解這個。但願這篇文章能讓你們更深刻了解關於HA方面的知識。

6、參考文獻

[1.] http://zh.hortonworks.com/blog/hdfs-metadata-directories-explained/
[2.] https://blog.cloudera.com/blog/2014/03/a-guide-to-checkpointing-in-hadoop/
[3.] https://www.ibm.com/developerworks/cn/opensource/os-cn-hadoop-name-node/

 

相關閱讀

基於 Hadoop大數據分析應用場景與實戰

大數據框架學習:從 Hadoop 到 Spark

從源碼中分析 Hadoop 的 RPC 機制


 

此文已由做者受權騰訊雲技術社區發佈,轉載請註明文章出處原文連接:https://cloud.tencent.com/community/article/282177

相關文章
相關標籤/搜索