1. 程式人生 > >HDFS NameNode 高併發資料讀寫架構及QJM選舉深入研究-Hadoop商業環境實戰

HDFS NameNode 高併發資料讀寫架構及QJM選舉深入研究-Hadoop商業環境實戰

本套技術專欄是作者(秦凱新)平時工作的總結和昇華,通過從真實商業環境抽取案例進行總結和分享,並給出商業應用的調優建議和叢集環境容量規劃等內容,請持續關注本套部落格。期待加入IOT時代最具戰鬥力的團隊。QQ郵箱地址:[email protected],如有任何學術交流,可隨時聯絡。

1 Hadoop 2.x 系統架構演進

  • 2.x版本中,HDFS架構解決了單點故障問題,即引入雙NameNode架構。
  • 同時藉助共享儲存系統來進行元資料的同步,共享儲存系統型別一般有幾類,如:Shared NAS+NFS、BookKeeper、BackupNode 和 Quorum Journal Manager(QJM),下圖中用的是QJM作為共享儲存元件,通過搭建奇數結點的JournalNode實現主備NameNode元資料操作資訊同步。

  • QJM全稱是Quorum Journal Manager, 由JournalNode(JN)組成,一般是奇數點結點組成。每個JournalNode對外有一個簡易的RPC介面,以供NameNode讀寫EditLog到JN本地磁碟。當寫EditLog時,NameNode會同時向所有JournalNode並行寫檔案。

2 高併發與QJM寫過程息息相關(高併發)

  • EditLog同時寫到本地和JournalNode兩處地方。
  • 寫本地由配置中引數dfs.namenode.name.dir控制。
  • 寫JN由引數dfs.namenode.shared.edits.dir控制。
  • 在寫EditLog時會由兩個不同的輸出流來控制日誌的寫過程,分別為:EditLogFileOutputStream(本地輸出流)和QuorumOutputStream(JN輸出流)。
  • NN寫EditLog也不是直接寫到磁碟中,為保證高吞吐,NameNode會分別為EditLogFileOutputStream和QuorumOutputStream定義兩個同等大小的Buffer,大小大概是512KB,一個寫Buffer(buffCurrent),一個同步Buffer(buffReady),這樣可以一邊寫一邊同步,所以EditLog是一個非同步寫過程,同時也是一個批量同步的過程,避免每寫一筆就同步一次日誌。
  • 雙寫雙同步過程:hadoop定義了一個緩衝區交換的過程,即bufferCurrent和buffReady。在達到條件時會觸發交換。一個負責EditLog寫入緩衝區,另外一個緩衝區負責寫入磁碟並同步到JournalNodes。
  • 如bufferCurrent在達到閾值同時bufferReady的資料又同步完時,bufferReady資料會清空,同時會將bufferCurrent指標指向bufferReady以滿足繼續寫,另外會將bufferReady指標指向bufferCurrent以提供繼續同步EditLog到JN。

  • NameNode使用多執行緒接收多個客戶端傳送過來的併發的請求,但是瓶頸卻出現在後續的寫edits log上。此時就會出現本地磁碟 + 網路傳輸給journalnodes,效能兩大瓶頸:磁碟寫 + 網路寫!

  • 分段加鎖機制 + 記憶體雙緩衝機制

  • 多個客戶端執行緒可以快速的獲取鎖,生成全域性鎖txid,然後快速的將edits log寫入記憶體緩衝。每一次只會有一個執行緒獲取全域性鎖,但是注意因為是寫緩衝區,所以很快就會釋放鎖。

  • 緩衝區的交換髮生在EditLog同步結束後,當來一個執行緒進行檢查有沒有誰在寫磁碟和網路,如果沒有了直接交換雙緩衝的區域buffCurrent和區域buffReady,接著第二次釋放鎖。這個過程相當快速,記憶體裡判斷幾個條件,耗時不了幾微秒。

  • 同步EditLog到JN時,有一個專有執行緒可以批量的將一個緩衝中的多條edits log刷入磁碟和網路。

  • 因此只要沒有執行緒在寫磁碟和網路,就達到了緩衝區交換的目的。

  • 也就因此達到了每秒數千次的高併發請求。

      詳情請參考石杉的架構筆記:https://juejin.im/post/5bec278c5188253e64332c76
    

3 QJM的隔離雙寫機制

  • 在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的發起的所有同步請求都會拒絕,實現隔離功能,防止了腦裂。

3 主備NN切換機制

  • 要完成HA,除了元資料同步外,還得有一個完備的主備切換機制,Hadoop的主備選舉依賴於ZooKeeper。
  • HealthMonitor: 監控NameNode健康狀態,若狀態異常會觸發回撥ZKFailoverController進行自動主備切換。
  • ActiveStandbyElector: 通知ZK執行主備選舉,若ZK完成變更,會回撥ZKFailoverController相應方法進行主備狀態切換。
  • 防腦裂: ZK本身是強一致和高可用的,可以用它來保證同一時刻只有一個活動節點。
  • ZKFailoverController: 是HealthMontior和ActiveStandbyElector的母體,執行具體的切換操作。

4 hadoop的Chunk緩衝機制(檔案上傳)

  • Chunk緩衝機制:hadoop支援超大規模的檔案上傳和資料寫入,那麼在HDFS客戶端原始碼中,內部實現了高效的chunk緩衝機制。上傳資料時首先會被寫入一個chunk緩衝陣列,這個chunk是一個512位元組0.5KB大小的資料片段,而最終的容器是一個緩衝陣列,一般為9個chunk的大小,該陣列可以容納多個chunk大小的資料在裡面緩衝。基於此機制就可以支援大資料量的檔案上傳。
  • 當chunk緩衝陣列都寫滿了之後,就會把這個chunk緩衝陣列根據chunk大小(512位元組0.5KB)切割為多個chunk,一個chunk是一個數據片段,預設是512位元組0.5KB。
  • 然後多個chunk會直接一次性寫入另外一個記憶體緩衝Packet資料包內,Packet資料包可以容納127個chunk,大小大致為64KB。
  • Packet資料包機制:通過這個Packet資料包機制的,可以在記憶體中容納大量的資料,避免了頻繁的網路傳輸。
  • 記憶體佇列非同步傳送機制:當一個Packet被塞滿了chunk之後,就會放進一個記憶體佇列進行FIFO排隊。
  • DataStreamer執行緒會不斷的獲取佇列中的Packet資料包,通過網路傳輸直接連續傳送Packet資料包給DataNode,當寫滿一個Block時,就會通知NameNode Block寫入完畢。
  • 本套技術專欄是作者(秦凱新)平時工作的總結和昇華,通過從真實商業環境抽取案例進行總結和分享,並給出商業應用的調優建議和叢集環境容量規劃等內容,請持續關注本套部落格。期待加入IOT時代最具戰鬥力的團隊。QQ郵箱地址:[email protected],如有任何學術交流,可隨時聯絡。

5 hadoop檔案契約機制(大規模HDFS檔案寫請求過期處理)

  • 多個客戶端同時併發的寫Hadoop HDFS上的同一個檔案是不被允許的,因為HDFS上的檔案是不允許併發寫的。
  • 通過檔案契約機制,可以保證同一時間只有一個客戶端執行緒訪問同一個檔案。
  • 該客戶端通過開啟一個契約後臺執行緒,不斷的進行檔案續約,如果某個契約很長時間沒續約了,此時就自動過期掉這個契約,讓其他的HDFS客戶端來寫。
  • 注意問題來了,一個nameNode內部可能有成千上萬個客戶端來同時修改hdfs上的成千上萬個檔案,這種情況下,如何保證契約後臺執行緒去優雅的檢查成千上萬個檔案的契約是否過期?
  • 在HDFS內部維護了一個TreeSet資料結構來根據最近一次續約時間對契約進行排序,因此往往最老的契約就排在了TreeSet的最前面,最近續約的契約就放在了TreeSet的最後面。每次檢查契約是否過期的時候,不用再遍歷成千上萬的契約,直接判斷TreeSet最前面契約還沒過期,那麼就不用繼續檢查了!
  • 如果一個契約過期了,那麼就刪掉最老契約,然後再檢查第二舊契約,依次類推。
  • TreeSet排序 + 優先檢查最舊契約的機制在支援大規模HDFS檔案寫請求上,有非常好的效能提升。

6 總結

一直想深入到Hadoop原始碼上,仔細的研究一番,奈何時間有限,大資料技術棧體系還沒有完全形成,希望我的部落格系列在完整成型後,深入的研究一波原始碼。辛苦成文,實屬不易。各自珍惜。

另外參考了大神的作品,來完成我的個人學習筆記,勿怪,謝謝!附上地址:https://juejin.im/post/5bf80bd66fb9a049ee801ad9。

本套技術專欄是作者(秦凱新)平時工作的總結和昇華,通過從真實商業環境抽取案例進行總結和分享,並給出商業應用的調優建議和叢集環境容量規劃等內容,請持續關注本套部落格。期待加入IOT時代最具戰鬥力的團隊。QQ郵箱地址:[email protected],如有任何學術交流,可隨時聯絡。

秦凱新 於深圳 201812020147