3.2 HDFS檔案讀寫
第3章 HDFS:分散式檔案系統
3.2 HDFS檔案讀寫
3.2.1 檔案訪問許可權
針對檔案和目錄,HDFS有與POSIX非常相似的許可權模式。
一共提供三類許可權模式:只讀許可權(r)、寫入許可權(w)和可執行許可權(x)。讀取檔案或列出目錄內容時需要只讀許可權。寫入一個檔案,或是在一個目錄上建立及刪除檔案或目錄,需要寫入許可權。對於檔案而言,可執行許可權可以忽略,因為你不能在HDFS中執行檔案(與POSIX不同),但在訪問一個目錄的子項時需要該許可權。
每個檔案和目錄都有所屬使用者(owner)、所屬組別(group)及模式(mode)。這個模式是由所屬使用者的許可權、組內成員的許可權及其他使用者的許可權組成的。
預設情況下,可以通過正在執行程序的使用者名稱和組名來唯一確定客戶端的標示。但由於客戶端是遠端的,任何使用者都可以簡單的在遠端系統上以他的名義建立一個賬戶來進行訪問。因此,作為共享檔案系統資源和防止資料意外損失的一種機制,許可權只能供合作團體中的使用者使用,而不能在一個不友好的環境中保護資源。注意,最新的hadoop系統支援kerberos使用者認證,該認證去除了這些限制。但是,除了上述限制之外,為防止使用者或者自動工具及程式意外修改或刪除檔案系統的重要部分,啟用許可權控制還是很重要的。
注意:這裡有一個超級使用者的概念,超級使用者是nameNode程序的標識。對於超級使用者,系統不會執行任何許可權檢查。
3.2.2 讀檔案
客戶端通過呼叫FileSystem物件的open()方法來開啟希望讀取的檔案,對於HDFS來說,這個物件是分散式檔案系統的一個例項。
DistributedFileSystem通過使用RPC來呼叫nameNode,以確定檔案起始塊的位置。對於每一個塊,nameNode返回存有該塊複本的datanode地址。此外,這些datanode根據他們與客戶端的距離來排序。如果客戶端本身就是一個datanode,並儲存有相應資料塊的一個複本時,該節點將從本地datanode中讀取資料。
DistributedFileSystem類返回一個FSDataInputStream物件給客戶端讀取資料。FSDataInputStream類轉而封裝DFSInputStream物件,該物件管理著datanode和nameNode的I/O。
接著,客戶端對這個輸入流呼叫read()方法。儲存著檔案起始塊的datanode地址的DFSInputStream隨即連線距離最近的datanode。通過對資料流反覆呼叫read()方法,可以將資料從datanode傳輸到客戶端。到達塊的末端時,DFSInputStream會關閉與該datanode的連線,然後尋找下一個塊的最佳datanode。客戶端只需要讀取連續的流,並且對於客戶端都是透明的。
客戶端從流中讀取資料時,塊是按照開啟DFSInputStream與datanode新建連線的順序讀取的。它也需要詢問namenode來檢索下一批所需塊的datanode的位置。一旦客戶端完成讀取,就對FSDataInputStream呼叫close()方法。
注意:在讀取資料的時候,如果DFSInputStream在與datanode通訊時遇到錯誤,它便會嘗試從這個塊的另外一個臨近datanode讀取資料。他也會記住那個故障datanode,以保證以後不會反覆讀取該節點上後續的塊。DFSInputStream也會通過校驗和確認從datanode傳送來的資料是否完整。如果發現一個損壞的塊, DFSInputStream就會在試圖從其他datanode讀取一個塊的複本之前通知namenode。
總結:在這個設計中,namenode會告知客戶端每個塊中最佳的datanode,並讓客戶端直接聯絡該datanode且檢索資料。由於資料流分散在該叢集中的所有datanode,所以這種設計會使HDFS可擴充套件到大量的併發客戶端。同時,namenode僅需要響應位置的請求(這些資訊儲存在記憶體中,非常高效),而無需響應資料請求,否則隨著客戶端數量的增長,namenode很快會成為一個瓶頸。
3.2.3 寫檔案
首先客戶端通過DistributedFileSystem上的create()方法指明一個預建立的檔案的檔名(第一步),DistributedFileSystem再通過RPC呼叫向NameNode申請建立一個新檔案(第二步,這時該檔案還沒有分配相應的block)。namenode檢查是否有同名檔案存在以及使用者是否有相應的建立許可權,如果檢查通過,namenode會為該檔案建立一個新的記錄,否則的話檔案建立失敗,客戶端得到一個IOException異常。DistributedFileSystem返回一個FSDataOutputStream以供客戶端寫入資料,與FSDataInputStream類似,FSDataOutputStream封裝了一個DFSOutputStream用於處理namenode與datanode之間的通訊。
當客戶端開始寫資料時(第三步),DFSOutputStream把寫入的資料分成包(packet), 放入一箇中間佇列——資料佇列(data queue)中去。DataStreamer從資料佇列中取資料,同時向namenode申請一個新的block來存放它已經取得的資料。namenode選擇一系列合適的datanode(個數由檔案的replica數決定)構成一個管道線(pipeline),這裡我們假設replica為3,所以管道線中就有三個datanode。DataSteamer把資料流式的寫入到管道線中的第一個datanode中(第四步),第一個datanode再把接收到的資料轉到第二個datanode中(第四步),以此類推。
DFSOutputStream同時也維護著另一箇中間佇列——確認佇列(ack queue),確認佇列中的包只有在得到管道線中所有的datanode的確認以後才會被移出確認佇列(第五步)。
如果某個datanode在寫資料的時候當掉了,下面這些對使用者透明的步驟會被執行:
管道線關閉,所有確認佇列上的資料會被挪到資料佇列的首部重新發送,這樣可以確保管道線中當掉的datanode下流的datanode不會因為當掉的datanode而丟失資料包。
在還在正常執行的datanode上的當前block上做一個標誌,這樣噹噹掉的datanode重新啟動以後namenode就會知道該datanode上哪個block是剛才當機時殘留下的區域性損壞block,從而可以把它刪掉。
已經當掉的datanode從管道線中被移除,未寫完的block的其他資料繼續被寫入到其他兩個還在正常執行的datanode中去,namenode知道這個block還處在under-replicated狀態(也即備份數不足的狀態)下,然後他會安排一個新的replica從而達到要求的備份數,後續的block寫入方法同前面正常時候一樣。
有可能管道線中的多個datanode當掉(雖然不太經常發生),但只要dfs.replication.min(預設為1)個replica被建立,我們就認為該建立成功了。剩餘的replica會在以後非同步建立以達到指定的replica數。
當客戶端完成寫資料後,它會呼叫close()方法(第六步)。這個操作會沖洗(flush)所有剩下的package到pipeline中,等待這些package確認成功,然後通知namenode寫入檔案成功(第七步)。這時候namenode就知道該檔案由哪些block組成(因為DataStreamer向namenode請求分配新block,namenode當然會知道它分配過哪些blcok給給定檔案),它會等待最少的replica數被建立,然後成功返回。
注意:①hdfs在寫入的過程中,有一點與hdfs讀取的時候非常相似,就是:DataStreamer在寫入資料的時候,每寫完一個datanode的資料塊(預設64M),都會重新向nameNode申請合適的datanode列表。這是為了保證系統中datanode資料儲存的均衡性。
②hdfs寫入過程中,datanode管線的確認應答包並不是每寫完一個datanode,就返回一個確認應答,而是一直寫入,直到最後一個datanode寫入完畢後,統一返回應答包。如果中間的一個datanode出現故障,那麼返回的應答就是前面完好的datanode確認應答,和故障datanode的故障異常。這樣我們也就可以理解,在寫入資料的過程中,為什麼資料包的校驗是在最後一個datanode完成
第3章 HDFS:分散式檔案系統
3.2 HDFS檔案讀寫
3.2.1 檔案訪問許可權
針對檔案和目錄,HDFS有與POSIX非常相似的許可權模式。
一共提供三類許可權模式:只讀許可權(r)、寫入許可權(w)和可執行許可權(x)。讀取檔案或列出目錄內容時需要只讀許可權。寫入一個檔案,或是在一個目錄上建立及刪除檔案或目錄,需要寫入許可權。對於檔案而言,可執行許可權可以忽略,因為你不能在HDFS中執行檔案(與POSIX不同),但在訪問一個目錄的子項時需要該許可權。
每個檔案和目錄都有所屬使用者(owner)、所屬組別(group)及模式(mode)。這個模式是由所屬使用者的許可權、組內成員的許可權及其他使用者的許可權組成的。
預設情況下,可以通過正在執行程序的使用者名稱和組名來唯一確定客戶端的標示。但由於客戶端是遠端的,任何使用者都可以簡單的在遠端系統上以他的名義建立一個賬戶來進行訪問。因此,作為共享檔案系統資源和防止資料意外損失的一種機制,許可權只能供合作團體中的使用者使用,而不能在一個不友好的環境中保護資源。注意,最新的hadoop系統支援kerberos使用者認證,該認證去除了這些限制。但是,除了上述限制之外,為防止使用者或者自動工具及程式意外修改或刪除檔案系統的重要部分,啟用許可權控制還是很重要的。
注意:這裡有一個超級使用者的概念,超級使用者是nameNode程序的標識。對於超級使用者,系統不會執行任何許可權檢查。
3.2.2 讀檔案
客戶端通過呼叫FileSystem物件的open()方法來開啟希望讀取的檔案,對於HDFS來說,這個物件是分散式檔案系統的一個例項。
DistributedFileSystem通過使用RPC來呼叫nameNode,以確定檔案起始塊的位置。對於每一個塊,nameNode返回存有該塊複本的datanode地址。此外,這些datanode根據他們與客戶端的距離來排序。如果客戶端本身就是一個datanode,並儲存有相應資料塊的一個複本時,該節點將從本地datanode中讀取資料。
DistributedFileSystem類返回一個FSDataInputStream物件給客戶端讀取資料。FSDataInputStream類轉而封裝DFSInputStream物件,該物件管理著datanode和nameNode的I/O。
接著,客戶端對這個輸入流呼叫read()方法。儲存著檔案起始塊的datanode地址的DFSInputStream隨即連線距離最近的datanode。通過對資料流反覆呼叫read()方法,可以將資料從datanode傳輸到客戶端。到達塊的末端時,DFSInputStream會關閉與該datanode的連線,然後尋找下一個塊的最佳datanode。客戶端只需要讀取連續的流,並且對於客戶端都是透明的。
客戶端從流中讀取資料時,塊是按照開啟DFSInputStream與datanode新建連線的順序讀取的。它也需要詢問namenode來檢索下一批所需塊的datanode的位置。一旦客戶端完成讀取,就對FSDataInputStream呼叫close()方法。
注意:在讀取資料的時候,如果DFSInputStream在與datanode通訊時遇到錯誤,它便會嘗試從這個塊的另外一個臨近datanode讀取資料。他也會記住那個故障datanode,以保證以後不會反覆讀取該節點上後續的塊。DFSInputStream也會通過校驗和確認從datanode傳送來的資料是否完整。如果發現一個損壞的塊, DFSInputStream就會在試圖從其他datanode讀取一個塊的複本之前通知namenode。
總結:在這個設計中,namenode會告知客戶端每個塊中最佳的datanode,並讓客戶端直接聯絡該datanode且檢索資料。由於資料流分散在該叢集中的所有datanode,所以這種設計會使HDFS可擴充套件到大量的併發客戶端。同時,namenode僅需要響應位置的請求(這些資訊儲存在記憶體中,非常高效),而無需響應資料請求,否則隨著客戶端數量的增長,namenode很快會成為一個瓶頸。
3.2.3 寫檔案
首先客戶端通過DistributedFileSystem上的create()方法指明一個預建立的檔案的檔名(第一步),DistributedFileSystem再通過RPC呼叫向NameNode申請建立一個新檔案(第二步,這時該檔案還沒有分配相應的block)。namenode檢查是否有同名檔案存在以及使用者是否有相應的建立許可權,如果檢查通過,namenode會為該檔案建立一個新的記錄,否則的話檔案建立失敗,客戶端得到一個IOException異常。DistributedFileSystem返回一個FSDataOutputStream以供客戶端寫入資料,與FSDataInputStream類似,FSDataOutputStream封裝了一個DFSOutputStream用於處理namenode與datanode之間的通訊。
當客戶端開始寫資料時(第三步),DFSOutputStream把寫入的資料分成包(packet), 放入一箇中間佇列——資料佇列(data queue)中去。DataStreamer從資料佇列中取資料,同時向namenode申請一個新的block來存放它已經取得的資料。namenode選擇一系列合適的datanode(個數由檔案的replica數決定)構成一個管道線(pipeline),這裡我們假設replica為3,所以管道線中就有三個datanode。DataSteamer把資料流式的寫入到管道線中的第一個datanode中(第四步),第一個datanode再把接收到的資料轉到第二個datanode中(第四步),以此類推。
DFSOutputStream同時也維護著另一箇中間佇列——確認佇列(ack queue),確認佇列中的包只有在得到管道線中所有的datanode的確認以後才會被移出確認佇列(第五步)。
如果某個datanode在寫資料的時候當掉了,下面這些對使用者透明的步驟會被執行:
管道線關閉,所有確認佇列上的資料會被挪到資料佇列的首部重新發送,這樣可以確保管道線中當掉的datanode下流的datanode不會因為當掉的datanode而丟失資料包。
在還在正常執行的datanode上的當前block上做一個標誌,這樣噹噹掉的datanode重新啟動以後namenode就會知道該datanode上哪個block是剛才當機時殘留下的區域性損壞block,從而可以把它刪掉。
已經當掉的datanode從管道線中被移除,未寫完的block的其他資料繼續被寫入到其他兩個還在正常執行的datanode中去,namenode知道這個block還處在under-replicated狀態(也即備份數不足的狀態)下,然後他會安排一個新的replica從而達到要求的備份數,後續的block寫入方法同前面正常時候一樣。
有可能管道線中的多個datanode當掉(雖然不太經常發生),但只要dfs.replication.min(預設為1)個replica被建立,我們就認為該建立成功了。剩餘的replica會在以後非同步建立以達到指定的replica數。
當客戶端完成寫資料後,它會呼叫close()方法(第六步)。這個操作會沖洗(flush)所有剩下的package到pipeline中,等待這些package確認成功,然後通知namenode寫入檔案成功(第七步)。這時候namenode就知道該檔案由哪些block組成(因為DataStreamer向namenode請求分配新block,namenode當然會知道它分配過哪些blcok給給定檔案),它會等待最少的replica數被建立,然後成功返回。
注意:①hdfs在寫入的過程中,有一點與hdfs讀取的時候非常相似,就是:DataStreamer在寫入資料的時候,每寫完一個datanode的資料塊(預設64M),都會重新向nameNode申請合適的datanode列表。這是為了保證系統中datanode資料儲存的均衡性。
②hdfs寫入過程中,datanode管線的確認應答包並不是每寫完一個datanode,就返回一個確認應答,而是一直寫入,直到最後一個datanode寫入完畢後,統一返回應答包。如果中間的一個datanode出現故障,那麼返回的應答就是前面完好的datanode確認應答,和故障datanode的故障異常。這樣我們也就可以理解,在寫入資料的過程中,為什麼資料包的校驗是在最後一個datanode完成