談一談 DataNode 如何向 NameNode 發(fā)送心跳的
本文轉(zhuǎn)載自微信公眾號「KK架構(gòu)師」,作者wangkai。轉(zhuǎn)載本文請聯(lián)系KK架構(gòu)師公眾號。
一、分布式系統(tǒng)中的心跳技術(shù)
心跳是分布式技術(shù)中常用的技術(shù)手段。心跳,顧名思義,就是以固定的頻率向其他節(jié)點(diǎn)匯報(bào)當(dāng)前節(jié)點(diǎn)狀態(tài)的方式。收到心跳,一般可以認(rèn)為發(fā)送心跳的這個節(jié)點(diǎn)在當(dāng)前的網(wǎng)絡(luò)拓?fù)渲惺橇己玫摹?/p>
當(dāng)然,心跳匯報(bào)時(shí),一般也會攜帶一些附加的狀態(tài)、元數(shù)據(jù)信息、當(dāng)前節(jié)點(diǎn)的信息,以便其他節(jié)點(diǎn)管理。
心跳有兩種方式:
- 單向的 heartbeat;
- 交互的 ping-pong;
第一種方式下,target 進(jìn)程需要定時(shí)給 detector 發(fā)送消息,告知自己的存活性。而 detector 無需給 target 回復(fù)任何消息,只是每隔一段時(shí)間去檢測 target 進(jìn)程有沒有匯報(bào)。
第二種方式更為常見,比如我們的 redis 就是采用這種方式:
- detector -> target: Are you ok?
- target -> detector: Yeah, pretty good.
Detector 發(fā)起檢測,如果 target 連續(xù) N 次不回復(fù)消息,那么 detector 就認(rèn)為其處于 non-active 狀態(tài)。
那么常用的心跳檢測機(jī)制有哪些:
(1)傳統(tǒng)的周期檢測心跳機(jī)制
其檢測方法很粗暴:設(shè)定一個超時(shí)時(shí)間 T,只要在 T 之內(nèi)沒有收到對方的心跳包便可認(rèn)為對方宕機(jī),方法簡單有效,使用比較廣泛。
所以這個方法的重點(diǎn)就在于這個超時(shí)時(shí)間 T 的設(shè)置,設(shè)置的太短了,有可能會因?yàn)楫?dāng)前網(wǎng)絡(luò)阻塞導(dǎo)致誤判,讓這個節(jié)點(diǎn)下線,產(chǎn)生其他不必要的后果;設(shè)置的太長,會導(dǎo)致判斷“遲緩”,所以需要綜合各種情況來權(quán)衡和設(shè)定。
HDFS 就是使用的這種心跳機(jī)制。
(2)累積失效檢測機(jī)制
隨著網(wǎng)路負(fù)載的加大,Server 心跳的接收時(shí)間可能會大于上限值 T;但當(dāng)網(wǎng)絡(luò)壓力減少時(shí),心跳接收時(shí)間又會小于 T ,如果用一成不變的T 來反映心跳狀況,則會造成判斷”遲緩“或誤判。這個時(shí)候我們可以計(jì)算心跳延遲的概率,用這個概率來判斷是否發(fā)生故障,提高準(zhǔn)確性。
二、DataNode 是如何向 NameNode 發(fā)送心跳的
我們從 hadoop 源碼看 DataNode 是如何發(fā)送心跳的
1、從 DataNode 類的 main 方法開始
2、創(chuàng)建 DataNode
3、實(shí)例化 DataNode
4、創(chuàng)建實(shí)例
5、 new 了 DataNode
6、 這個方法構(gòu)造函數(shù)有點(diǎn)長,拉到最下面
7、然后來到這個方法里
這個方法表面看起來是刷新 NameNode,實(shí)際上里面做了兩件事情,把自己注冊到 NameNode 上,另外一件事情是向 NameNode 定時(shí)發(fā)送心跳。
8、點(diǎn)進(jìn)去,現(xiàn)在是在 BlockPoolManager 里面
9、再來到這個方法里
10、發(fā)現(xiàn)它在遍歷這個 BPOfferService
這里大致說明一下 offerServices 是個什么數(shù)據(jù)結(jié)構(gòu)。
用一張圖來表示吧
那個 offerServices 其實(shí)裝的就是最左邊的 BPOfferService。
每個 BPOfferService 里面有兩個 BpServiceActor,每個 BpServiceActor 對應(yīng)一個 NameNode。
如果是上圖中的高可用,那么一主一備兩個 NameNode,分別對應(yīng)一個 BpServiceActor。
所以遍歷 offerService 其實(shí)就是在遍歷整個集群每個聯(lián)邦的每個 NameNode 節(jié)點(diǎn)。
11、開始遍歷(當(dāng)前類:BpOfferService)
12、線程 start(當(dāng)前類:BPServiceActor)
再點(diǎn)進(jìn)去已經(jīng)是 Thread 的方法了,可見它其實(shí)是個線程。
那我們應(yīng)該看這個類的 run 方法。
13、線程的運(yùn)行內(nèi)容
run 方法的上半部分是往 NameNode 注冊,下半部分是發(fā)送心跳
14、發(fā)送心跳
可以看到這是一個 while 循環(huán),每隔一段時(shí)間(dnConf.heartBeatInterval = 3秒)就會執(zhí)行一次
可以看到 DataNode 的心跳就是采用了周期性檢測機(jī)制,每隔 3 s ,往所有的 NameNode 發(fā)送心跳。
15、再點(diǎn)進(jìn)去,已經(jīng)是 NameNode 的 proxy 代理方法了。
因?yàn)樗强?rpc 通信的,此時(shí) DataNode 是客戶端,NameNode 是服務(wù)端。
這個時(shí)候,我們應(yīng)該看 NameNodeRpcServer 方法,具體的實(shí)現(xiàn)是在這個方法里的。
16、看服務(wù)端的處理,此時(shí)我們在 NameNodeRpcServer 類中
這個方法里的具體就不看了,(其實(shí)也很重要的的)。
大致就是把 DataNode 心跳包的基本信息(比如本節(jié)點(diǎn)的存儲容量信息等)更新到 NameNode 對應(yīng)的結(jié)構(gòu)中。
并且更新上一次心跳時(shí)間,以便下次判斷 DataNode 是否心跳超時(shí)。
其實(shí)心跳就是這么樸素了。
最后,NameNode 會在心跳的響應(yīng)中,告訴 DataNode 應(yīng)該做些什么事情,比如把本節(jié)點(diǎn)的 Block 備份到其他節(jié)點(diǎn)上去。
也就是說,NameNode 本身不會和 DataNode 通信,而是在心跳信息中告訴 DataNode 該做什么。
三、小結(jié)
本次通過瀏覽 DataNode 代碼了,知道了其實(shí) DataNode 的心跳,就是DataNode 在后臺啟動了線程,定時(shí)向整個集群所有的 NameNode 發(fā)送心跳信息,NameNode 會在心跳響應(yīng)信息中告訴 DataNode 本次該做些什么事情。






