Hadoop源碼篇 --- DataNode的初始化與註冊流程

前言

由於你們讀源碼的方式都各有千秋,這裏的閱讀過程並不表明最佳實踐,只是一個自身閱讀過程的再現而已。因此若是有一些遺漏的重點,也能夠在留言處替我指出。先前也有不少小夥伴提出了一些個人錯誤或者改進的地方,這裏表示衷心的感謝。node

那按照先前的套路,咱們提出兩個任務,整篇就爲了完成驗證下面的任務而進行:linux

一、DataNode初始化:咱們平時搭建集羣時,經過jps命令時能夠看到DataNode的服務的,因此DataNode就應該是RPC的服務端apache

二、DataNode的註冊:HDFS是一個主從架構,NameNode是主節點而DataNode是從節點,因此DataNode啓動時是須要和NameNode進行註冊的安全

三、心跳機制:從節點須要發送心跳讓主節點得知從節點的存活數據結構

四、NameNode是如何管理元數據的,HA高可用方案的實現原理架構

(補充)Hadoop HA高可用方案原理

先把一個簡單的給搞定吧,由於直接上源碼真的挺犯困的併發

在Hadoop1.x的時候,咱們只有一個NameNode和DataNode,NameNode負責管理元數據,DataNode用於存儲數據,爲了保證數據安全,每一個副本會存在三個備份,每一個block佔據64M的大小oop

這樣NameNode就會存在單點故障的問題,因此這時候hadoop的團隊就開始解決這個問題了,由於NameNode是管理着集羣的元數據的,這是一個有狀態的服務,這就說明了它是不能隨便中止服務的,那既然問題實際上是算只有一個NameNode很差幹活的鍋,那咱們增長NameNode數量不就行了?但是無緣無故地增長一個NameNode,它倆如何可以保證元數據的一致性呢?post

解決這個單點的問題,首先就是這兩個NameNode如何保證元數據的一致,此時解決方案有三個線程

① 使用linux的共享存儲目錄

這個作法其實就是讓咱們的集羣元數據不存放於Namenode中

而是放在一個共享的存儲單元中,這是apache官方當時推薦的作法,但是你們都沒吃它的這一套。如今確定是沒人再這麼玩的了

② 使用zookeeper集羣

這也很簡單,就是咱們的一個NameNode去往zookeeper集羣中寫元數據,而後另一個就去zookeeper裏面去讀,有部分公司確實也是在採用這個方案

③ cloudera QJM

第三種方案實際上是由cloudera這家公司所提出的,它經過一個Journalnode集羣來完成,這個東西和zookeeper其實差不太多,實現邏輯也基本雷同

並且它自己也是一個集羣,健康依據爲過半節點存活便可,好比我圖中的3臺掛掉一臺,那就是2/3>0.5,那此時判斷這個集羣是健康狀態。

有可能你會問,爲何你就這樣草率地決定了第一個NameNode是寫元數據的第二個是同步元數據的呢?由於它們兩個還有各自的狀態問題

此時active狀態下的NameNode負責往集羣寫,而standby的從集羣讀

固然如今咱們的問題解決了嗎,其實並無,若是NameNode(active)在某天凌晨忽然宕機,那我豈不是凌晨就要打開電腦,連上公司的服務,使用命令強行把standby狀態下的NameNode設置爲active集羣才恢復正常工做了?

那咱們如今如何保證NameNode狀態的自動切換呢?

④ 實現NameNode狀態的切換

此時咱們又引入了老朋友zookeeper,真是哪都有它,在zookeeper中建立一個鎖的目錄,而後NameNode啓動的時候都會過去搶佔鎖,兩個NameNode誰先搶到,誰就是active狀態。

並且每個NameNode上還有一個ZKFC的服務,持續監聽NameNode的健康狀態,若是active NameNode出現問題,ZKFC將會報告給zookeeper,而後zookeeper會將鎖分配給standby的NameNode上。使其自動切換爲active狀態

1、DataNode的初始化(源碼片斷基於Hadoop2.7.0)

咱們直接找到DataNode的main方法處,它和NameNode差很少,進行一個參數的判斷以後,不知足就退出。除了這麼一句,就還剩這麼一個secureMain(args,null)了,那核心代碼就是這一個

點進去發現了一個creataDataNode,因此我就最喜歡這種命名這麼直接的,那就點進去creataDataNode吧

把註釋先複製一下,粘貼到百度

那咱們一看,實例化的英文不就是instantiate嘛,因此不用想太多,點進去就是了,下面的那個if也很簡單,由於當這個實例化成功,DataNode不爲null,那就把這個DataNode啓動起來唄,就是這麼簡單而已,這個啓動其實就是就是一堆線程

看源碼的時候要帶着目的,否則就會被其餘的一些代碼帶跑,有try看try,還有就是一大串單詞的時候看最後一個單詞判斷它的做用,好比前面的if,Configuration是配置呀,args是參數集啊,這些咱們都不關心。咱們如今就想知道你怎麼實例化,因此就盯着這個instance這玩意點就是了

同理,前面那些permission權限,checker檢查器咱們也不關心,看到最後是返回一個DataNode便可。那咱們就點這個

映入眼簾的是一堆參數的配置,先無論,拉到咱們想看的位置,大概到465行左右會有一個try,咱們瞧瞧

StartDataNode到底經歷了幾個過程

看到啓動DataNode的代碼了,因此這個就是咱們想看的

① (補充)DataXceiver

補充一下:大體拉到1182行,這個initDataXceiver(conf)是初始化了咱們的DataXceiver,這個是幹啥用的呢,點進去

咱們知道,NameNode只是負責管理集羣中的元數據信息的,而真正存儲的數據是存儲在DataNode上的,實際上DataNode就是經過這個DataXceiver的服務來接收上傳上來的數據的

在974行左右有一段設置爲後臺線程的代碼,這個意思其實就是這個線程和主線程共存亡,若是主線程結束了,這個線程也會隨之中止。

② (補充)startInfoServer

回到StartDataNode的那個位置

相信你必定還有印象,就是在咱們的 Hadoop源碼篇 --- NameNode的啓動流程 1.4.1解析NameNode啓動流程中,也存在相似的一個startHttpServer,當時這個startHttpServer就是綁定了不少servlet,來加強自身的功能。而咱們的DataNode它也是同樣,會綁定servlet加強自身

眼尖的小夥伴確定就看到了一些熟悉的名詞,checksum不就是咱們對數據進行完整性校驗所使用的校驗和嘛,這說明獲取校驗和這個功能也是綁定上去的一個servlet

③ 初始化RPC的服務

再次回到StartDataNode的那個位置,看到initIpcServer

initIpcServer是啓動RPC的服務使用的。

這個代碼咱們又很熟悉了,這和 Hadoop源碼篇 --- NameNode的啓動流程 1.6.2 驗證存在設置參數的過程又是十分相似。並且一樣的建立服務端完成以後,它也綁定了不少的協議,好比DataNode和DataNode之間進行通訊的interDatanodeProtocolXlator,並且一樣這些協議也有一個惟一的versionID

④ 建立BlockPoolManager

blockManager的做用在註釋上已經給出來了,咱們點進去refreshNamenodes瞧瞧

看見do,繼續點進去,這個東西有100多行代碼,須要分點說明,可是僅說明最重要的兩個點

判斷存在着多少份不一樣的元數據

把註釋給翻譯一下,大體意思就是,它會判斷對於每個新的nameservices究竟是對已有的nameservices的更新仍是一個全新的nameservices

咱們平時搭建的HDFS集羣是HA高可用架構的,即NameNode分爲active和standby兩個,這兩個管理的元數據也是同樣的,因此它們管理的是同一個nameservices(在這裏這麼理解,nameservices就是存放元數據的一個目錄便可)。而對於聯邦來講,每個聯邦都會管理着一份元數據,兩個聯邦那天然就會存在兩份不同的元數據。

遍歷聯邦

針對每個聯邦建立一個BPOfferService,針對聯邦裏的每一個NameNode(其實也就2個)建立一個BPServiceActor

到這裏初始化的步驟就已經走完了,是否是感受一臉蒙圈,沒事,還有註冊流程

2、DataNode的註冊流程

剛剛的位置結尾有一個startAll(),這就是註冊的主要邏輯,點進去

能夠看到這裏它遍歷了聯邦,而後咱們點進去start()方法

連起來就是,先遍歷聯邦,再遍歷聯邦裏面的NameNode,而後將DataNode分別註冊進去遍歷出來的NameNode,繼續點start

調用線程的start方法實際上就是調用run方法,這個你們應該仍是知道的。

這裏的connectToNNAndHandshake()就是註冊的核心代碼了,直譯過來呢就是和NameNode進行握手。這裏使用了一個死循環來保證這個註冊必定可以被執行,若是出現異常,設置回睡眠5秒以後再次嘗試。若是執行成功,就break。。反正我就死賴在這裏了,就必定要你執行成功。

2.1 connectToNNAndHandshake()

既然剛剛都不惜使用死循環來讓connectToNNAndHandshake()必定要執行成功,那就點進來瞧瞧吧

此時咱們的主線目標就是調用NameNode的方法將DataNode給註冊進去,其實就是往NameNode存儲這個DataNode的信息。這裏咱們獲取了NameNode的代理,爲何這塊須要用到代理呢

代理對象角色內部含有對真實對象的引用,從而能夠操做真實對象,同時代理對象提供與真實對象相同的接口以便在任什麼時候刻都能代替真實對象。同時,代理對象能夠在執行真實對象操做時,附加其餘的操做,至關於對真實對象進行封裝。

因此此時咱們獲取到了一個bpNameNode的代理對象,而後經過register()往上面進行註冊

2.2 DataNode的註冊信息createRegistration

一直往下點能夠看到DatanodeRegistration類的屬性字段,有興趣的能夠去了解一下

並且咱們也能夠發現,在這個過程當中,咱們的主機名,端口號···等信息都已經一併發送過去了

獲取完DataNode信息後的registerDatanode

回到register()

由於向NameNode進行註冊的代碼也是十分重要,因此使用了和剛剛同樣的死循環套路,保證中間的註冊過程必定被執行,成功就break,異常就延遲一秒後重試。

此時咱們看到經過bpNameNode這個對象調用了一個registerDatanode方法,這裏明顯是調用了NamenodeRpcServer裏的同名方法的。不信咱們點進去,打開NamenodeRpcServer而後ctrl+f這個registerDatanode便可

這裏繼續點進去

有try看try,這裏明顯是DataNodeManager來處理關於DataNode的問題的,繼續點進去register

裏面代碼比較長,咱們一直拉到大約995行處,能夠看到一個addDataNode

addDatanode(nodeDescr)

參數nodeDescr就是剛剛createRegistration中提到的封裝好的一個DataNode的註冊信息

註冊信息的方式其實就是往這些數據結構中去填,好比這個什麼datanodeMap,點進去一看

這明顯就是第一個參數爲這個DataNode的惟一標識,第二個參數DataNode的description描述,就確定是這DataNode的註冊信息,把這些信息分別存儲在不一樣的數據結構中

addDatanode(nodeDescr)

這裏的參數nodeDescr和上面是同樣的,都是DataNode的註冊信息

好處就是之後遍歷心跳的信息的話就直接遍歷DatanodeDescriptor這個數據結構而不用再先遍歷DataNode而後再一個個取它們的心跳信息出來

總得來講,註冊其實就是往各個數據結構存放信息,寫入完成後,註冊就完成了

3、開始發送心跳

跳轉回 2.1 connectToNNAndHandshake() 的那裏往下看,也就是BPServiceActor類的大概890行。若是connectToNNAndHandshake成功,就break掉,開始發送心跳的步驟

這個循環就真的是一個死循環了,連break都沒有,出現異常也只會重試,因此咱們點進去看看

DataNode和NameNode的心跳間隔

在if (startTime - lastHeartbeat >= dnConf.heartBeatInterval)是判斷若是當前時間-最後一次心跳的時間大於dnConf.heartBeatInterval,就執行,因此咱們看看這個heartBeatInterval究竟是多長,點進去

看一下這個 DFS_HEARTBEAT_INTERVAL_DEFAULT 默認值爲3

因此之後我們就知道了,DataNode和NameNode保持心跳實際上是3秒一次的

以後就是 HeartbeatResponse resp = sendHeartBeat() 這句,它這個 HeartbeatResponse 就是NameNode給DataNode下發的指令,點進去sendHeartBeat()

發現其實心跳時會連帶這把DataNode的一些信息也附帶過去,好比說第一個reports是報告的意思,點進去看看

你看,其實就是內存容量,內存使用狀況···等等這些信息

發送心跳 sendHeartbeat

經過NameNode的代理對象bpNamenode調用發送心跳,那其實就是直接調用NameNode的sendHeartbeat方法,咱們直接來到NamenodeRpcServer找到同名方法

那些參數也一併帶進來了,咱們繼續點

這裏的cmds就是NameNode但願DataNode進行操做的指令,它會做爲一個參數封裝成一個HeartbeatResponse對象返回給DataNode

咱們嘗試着看看處理心跳的方法

處理心跳 handleHeartbeat

看到這裏其實都有種一直在看重複代碼的感受,不過讀源碼就是那麼糾結的過程

第一個getDatanode是根據每個DataNode獨一無二的id號來取出對應的DataNode

updateHeartbeat這個方法一直往下點能夠點到這個更新心跳狀態的這個方法

這裏由於咱們的DataNode一直是在工做的,它必須保持本身的狀態更新,而後很是重要的就是它必須修改上一次的心跳時間,由於咱們剛剛也看過了,咱們是經過當前時間-上一次心跳時間=3s的話,就會再次發送心跳,因此這一步很是重要。並且咱們也須要用到心跳這項指標來判斷這個DataNode節點是否存活,由於若是超過了必定的時間(這個值是本身配置的)未創建心跳,NameNode是會斷定這個節點掛掉而後它再讓其餘的block複製多一份數據的。

那到這裏其實DataNode就差很少了。

finally

相關文章
相關標籤/搜索