在Hadoop集群從1.0升級到2.0之后,我們一直在解決很多很多的問題。在今年8月初,我們檢測到線上頻繁有機器變成死亡結(jié)點,一段時間后自動恢復。進入死亡結(jié)點狀態(tài)的DataNode將不能讀寫數(shù)據(jù)塊。我們觀察了一下日志,看到DataNode中打印出很多接受數(shù)據(jù)快傳輸?shù)木€
在Hadoop集群從1.0升級到2.0之后,我們一直在解決很多很多的問題。在今年8月初,我們檢測到線上頻繁有機器變成死亡結(jié)點,一段時間后自動恢復。進入死亡結(jié)點狀態(tài)的DataNode將不能讀寫數(shù)據(jù)塊。我們觀察了一下日志,看到DataNode中打印出很多接受數(shù)據(jù)快傳輸?shù)木€程(DataXceiver),線程都是在Receiving的狀態(tài),而沒有結(jié)束。估摸了一下在死亡結(jié)點發(fā)生的階段大約有300個左右的線程積累下來。但是,沒找到其它突破口。
由于,HDFS的Client會自動重試。如果一個結(jié)點進入死亡結(jié)點,只要另外的數(shù)據(jù)塊的結(jié)點依然可讀,Client還是可以讀取到數(shù)據(jù)塊的。所以,死亡結(jié)點的問題對線上業(yè)務(wù)沒有造成影響。當時,還有其它優(yōu)先級更高的事情,所以,問題轉(zhuǎn)為觀察狀態(tài)。
然后終于在一次機房意外斷電,集群重啟之后,一個線上的作業(yè)報找不到數(shù)據(jù)塊。經(jīng)日志確認,產(chǎn)生的原因是擁有這個數(shù)據(jù)塊副本的兩個機器同時進入死亡結(jié)點! 于是,問題轉(zhuǎn)入高優(yōu)先級,優(yōu)先解決。
首先知道,DataNode進入死亡結(jié)點狀態(tài)是因為NameNode長期接收不到DataNode的心跳包,就會把DataNode歸入死亡結(jié)點。而DataNode的心跳線程是單獨一個線程。
現(xiàn)象的最后一點,6小時的間隔,可謂是這個問題的突破點。在配置文件中找到6小時的間隔的工作有兩種:
根據(jù)兩者打印的日志和死亡結(jié)點發(fā)生的時間進行精確對比,發(fā)現(xiàn)后者的時間基本吻合。 然后,我們在集中查看磁盤掃描(DirectoryScanner)的代碼。
描述一下磁盤掃描的工作流程:
第一步,主線程和線程池的線程都是Daemon線程。Daemon線程的默認優(yōu)先級比較低。
第二步,由于涉及到磁盤讀寫。如果,外部磁盤壓力大的時候,會拖慢整個進度。但是,整個過程沒有加鎖。不可能對其它線程產(chǎn)生影響。
第四步,數(shù)據(jù)塊對比過程,為了阻止對blockMap的修改,整個過程針對DataSet對象加鎖(DataSet對象是DataNode中保存所有數(shù)據(jù)塊信息的內(nèi)存對象)。
那心跳進程為什么會使用DataSet的對象鎖? 我們寫了個小程序測試,在對DataSet加鎖的情況下,啟動心跳線程。發(fā)現(xiàn)心跳線程在獲取磁盤的可用空間的時候,需要獲得DataSet的鎖。
于是,問題變得清晰了:在6小時一次的磁盤掃描中,由于DirectoryScanner長久占用了DataSet的鎖,導致心跳線程不能發(fā)出心跳包。DataNode進入死亡結(jié)點狀態(tài)。而問題頻發(fā)在磁盤較多的機器是因為,數(shù)據(jù)塊數(shù)量和對比的過程的耗時相關(guān)。那是什么原因?qū)е翫irectoryScanner長久占用了DataSet的鎖呢?
我們觀察了加鎖部分的代碼,沒有找到磁盤操作。我們估摸了下,最多數(shù)據(jù)塊的機器也才80W左右各數(shù)據(jù)塊。如果是純內(nèi)存操作,不可能占用鎖長達10分鐘甚至30分鐘之久。
然后我們將懷疑的地方鎖定在主線程的Daemon屬性。因為,Daemon屬性的線程優(yōu)先級較低,懷疑是主線程在多線程的情況下,分配不到CPU時間片。
于是,我們作出第一個修改:將主線程改為普通線程的優(yōu)先級。
上線第二天,死亡結(jié)點現(xiàn)象還是出現(xiàn),現(xiàn)象出現(xiàn)的時間相對來說是短了點,但還是不能解決問題。
于是,我們開了個大招:針對死亡結(jié)點頻發(fā)的結(jié)點,加上一個每分鐘打印一次DataNode的jstack的腳本。
終于我們捕獲了在死亡結(jié)點發(fā)生時候的幾個堆棧。經(jīng)過對比分析,得出的結(jié)論是:
(呵呵)數(shù)據(jù)塊對比過程中,有一個使用Java的File對象的獲取文件長度的getlength方法。而這個方法是直接調(diào)用一個native方法,獲取磁盤上文件的長度。
當初我們就猜想,加鎖部分是否有磁盤的IO操作。因為IO操作的快慢,會受到當時的機器狀態(tài)影響很大。不得不說,這個位置太隱蔽了??戳撕芫枚紱]發(fā)現(xiàn),還好有jstack截獲出來。
6小時一次的DirectoryScanner在數(shù)據(jù)塊對比過程中,會對DataSet加鎖。如果,機器的磁盤壓力很高的情況下,對比過程中的磁盤操作十分耗時。導致DirectoryScanner長期持有DataSet的鎖,阻塞心跳線程和所有的DataXceiver的線程。DataNode變成死亡結(jié)點。一段時間后,對比過程結(jié)束。DataSet鎖釋放,DataNode回歸正常工作。
知道問題了就好解決了。我們采取的方式是把getlength操作提取到第二步的線程池的異步磁盤掃描中進行。
部署到線上后,數(shù)據(jù)對比時間降低到2秒左右。至此,死亡結(jié)點問題解決!
后續(xù)我們把Patch提交到Hadoop社區(qū)HDFS-5341,其中蹩腳的英語語法請大家無視。
原文地址:解決HDFS磁盤掃描導致死亡結(jié)點的問題, 感謝原作者分享。
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com