說起hadoop這個東西,只能說真是個偉大的發明,而本人對cutting大神也是無比的崇拜,記得剛接觸hadoop的時候,還覺得這個東西挺多餘的,但是現在想想,這個想法略傻逼...... 2006-2016,hadoop至今已經走過了10個年頭,版本也已經發展到2.7了,現在hadoop3.0也快出 ...
說起hadoop這個東西,只能說真是個偉大的發明,而本人對cutting大神也是無比的崇拜,記得剛接觸hadoop的時候,還覺得這個東西挺多餘的,但是現在想想,這個想法略傻逼......
2006-2016,hadoop至今已經走過了10個年頭,版本也已經發展到2.7了,現在hadoop3.0也快出來了,雖然spark,flink這些優秀的框架近幾年的勢頭非常的強勁,但是我認為,近幾年內並不會有哪個框架會取代hadoop,所以其實還是挺值得研究的。
那麼我這系列的文章呢,主要是想講講Hadoop的核心組件HDFS,這個文件系統現在應用真是非常的廣泛,特別是hadoop由1.x升到2.x之後,hdfs不論是從容錯性、可靠性、可擴展性都有了非常大的提升,體繫結構也有了很大的變化。正巧最近忙裡偷閑閱讀了一下hdfs的源代碼和看了一本介紹hdfs源代碼的書,希望分享下我自己的一些理解吧。
首先簡單說說HDFS的基本結構吧,主從式架構相信大家都非常熟悉,hadoop也是採用這一個,由namenode和datanode組成,但是在1.x版本中已經證實了僅僅依靠secondary namenode來保證hadoop集群的可靠性是遠遠不夠的,因為secondary namenode不是熱備份,而只是幫助namenode恢複數據而已,並且數據也不是恢復到最新的數據(有關secondary namenode的幫助恢復的工作原理有興趣的朋友可以留留言,我可以簡要介紹下,要是大家都有這個需求我就另發一篇講講這個secondary namenode,不過這個都是1.x的事了,大家應該不感興趣吧哈哈~),因此,要保證hadoop集群的高可用,在2.x中引入了HA機制,有關這個HA機制,主要是以來zookeeper來實現的,這裡先不細講,後面我會專門寫文章講這個的,這裡主要HDFS結構中幾種節點的通信。
那麼HDFS通信協議呢,有兩種,一種是Hadoop RPC介面,一種是流式介面,那麼這兩種介面各自有各自的分工,前者主要是負責一些連接的管理、節點的管理以及一些數據的管理,而後者主要是數據的讀寫傳輸。
1.Hadoop RPC 介面
首先,不同於流式介面,Hadoop RPC介面是基於protobuf實現的,protobuf是google的一種數據格式,這裡不做細究。那麼Hadoop RPC的介面主要有那麼幾個,包括ClientProtocol,ClientDatanodeProtocol,DatanodeProtocol,InterDatanodeProtocol,NamenodeProtocol這幾個,這幾個介面都是節點間的主要通信介面,其他的一些涉及安全、HA的介面我們以後在討論。
首先是重中之重的ClentProtocol,為什麼是重中之重呢?我們需要對數據文件做的操作基本上都是靠這個介面來實現的,我看的源代碼是2.6.4,大致數了下,這個介面有89十個方法,醉了......這裡主要的一些方法有getBlockLocations()、create()、append()、addBlock()、comlete()等等,具體這些方法怎麼我家下來介紹完集中介面之後,結合HDFS讀文件和寫文件的流程來介紹這些方法的使用。
然後是ClientDatanodeProtocol介面,這個介面是Client端和Datanode端通信使用的,主要有getReplicationVisibleLength()、getBlockLocalPathInfo()、refreshNamenodes()、deleteBlockPool()、getHdfsBlocksMetadata()、shutdownDatanode()這麼些方法,我們從這些方法名可以看到,這些方法基本上都是與數據塊的管理相關,很顯然嘛,Datanode主要的用途就是存儲數據嘛,他又不能自己管理數據。
那麼接下來就是datanode和namenode通信的介面,DatanodeProtocol,這個介面也是非常重用,解決了很多的問題,datanode的註冊、心跳應答數據塊彙報都是靠這個介面完成的。這個介面里,有datanode啟動相關的,心跳相關的和數據塊讀寫相關的方法。啟動相關的方法,其實主要是四個,versionRequest()、registerDatanode()、blockReport()和cacheReport(),按流程來說就是先是versionRequest(),確認namenode和datanode的版本信息是否一致,如果一直,則建立連接,然後是registerDatanode(),從名字也能看得出,這個方法是拿來註冊這個datanode節點的,註冊了之後namenode中才會有這個節點相關的信息,然後是blockReport()和cacheReport(),datanode彙報自己節點上的數據塊信息(有人很疑問,為啥一個新的節點要彙報數據塊信息?我認為應該是有些節點是因為失效了又重新加入集群中,所以裡面本來就有數據)。通過這四步,datanode就成功啟動加入集群了。心跳相關的方法其實主要就一個sendHeartbeat(),這個方法就是用來發送心跳的,心跳是預設3秒鐘一次。最後是數據塊讀寫相關的方法,有reportBadBlocks()、blockReceivedAndDeleted()和commitBlockSynchronization()方法,這些方法其實都是拿來管理數據塊的,比如出現無效的數據塊或者寫數據過程中節點故障數據沒寫完等等。
然後是InterDatanodeProtocol介面,這個介面很簡單,就是datanode之間相互通信的介面,雖然這個介面簡單,但是其實很有用,因為我們所說的副本就是通過datanode之間的通信來實現複製的而不是通過namenode同時將文件數據寫到三個副本中。
最後就是NamenodeProtocol了,這個介面就不說了吧,在2.x都沒什麼用了,這個是namenode和secondary namenode通信的介面。
2.流式介面
流式介面有兩種,一種是基於TCP的DataTransferProtocol,一種是HA機制的active namenode和standby namenode間的HTTP介面,第二種先不說,因為涉及HA機制節點的切換以及fsimage和editlog的合併方式等等,這個今後另起一篇來說。
那麼就是DataTransferProtocol了,這個介面最主要的方法就是readBlock()、writeBlock()和transferBlock()了。讀數據塊、寫數據塊以及數據塊額複製就是靠這些方法來實現。
介紹完兩類介面之後,我們應該是還有一個問題沒有解決吧,嘿嘿,hdfs的讀寫文件問題。二話不說,先來兩張圖:
第一張圖是讀數據的,第二張圖是寫數據的,這兩張圖是官方給的圖。
先說說讀文件,首先是HDFS客戶端會調用DistributedFileSystem.open()打開跟集群的連接,並且打開文件,這個方法底層來說會調用ClientProtocol介面的open()方法,然後返回一個數據流給客戶端,此時客戶端會在調用介面的getBlockLocations()方法得到文件的一個數據塊的位置等等信息,然後客戶端就會通過數據流調用read()方法從這些位置信息裡面選出一個最有的節點來進行數據讀取(一般三個副本位置會選取網路開銷最少的那個節點,本地節點就好了),傳輸完畢之後,客戶端會再調用getBlockLocations()方法得到下一個數據塊的位置信息,然後開始讀,知道數據讀取結束,客戶端調用close()方法,關閉數據流。
說完讀文件,就是寫文件了,寫文件就稍微比讀文件要複雜一些。首先客戶端會調用DistributedFileSystem.create()方法在hdfs中創建一個新的空文件,這個方法會在底層調用ClientProtocol.create()方法,namenode會在文件目錄樹下添加一個新的文件,並且將操作更新到editlog中,此舉之後,集群會返回一個數據輸出流,然後客戶端就可以開始通過調用write()方法寫數據到數據流中了,但是此時namenode中並沒有任何這個數據的數據塊元數據映射,所以數據流會調用addBlock()方法獲取要寫入datanode節點的信息,然後就是write()方法的調用寫數據了,寫到一個節點上之後,這個節點就開始建立與另外的datanode的連接,然後將數據複製到其他datanode,從而實現副本。當副本寫完之後,第一個datanode就會返回一個確認包,確認數據已經寫入完畢,並且調用blockReceivedAndDeleted()方法告訴namenode要更新記憶體元數據的數據,然後開始下一個數據塊的寫入,當數據寫入完畢之後,調用close()方法關閉數據流。
追加寫文件其實流程上跟寫文件差不多,這裡就不多做贅述了。
好了,寫了那麼多,主要是一個warming up,大致介紹下hdfs的一些基本原理和流程,接下來的文章里,希望能夠更大家分享一些更細節的東西和hdfs內部的一些實現。共勉~
PS:本人的博客會先更新在簡書上然後再更新博客,個人感覺簡書對於知識的傳播還是挺方便的哈哈~