HDFS-剖析文件寫入(寫流程)

寫流程解析

public static void main(String[] args) throws Exception {
    //加載本地指定目錄下的文件
    InputStream is = new BufferedInputStream(new FileInputStream("D:/123/word.txt"));
    //獲取配置文件
    Configuration conf = new Configuration();
    //獲取分佈式文件系統
    FileSystem fs = FileSystem.get(conf);
    //指定HDFS文件系統上的目標路徑
    Path des = new Path("/123/word.txt");
    //獲取輸出流對象
    FSDataOutputStream fsdos = fs.create(des);
    //開始上傳
    IOUtils.copyBytes(is, fsdos, 4096, false);
    //關閉流對象
    IOUtils.closeStream(fsdos);
}

在這裏插入圖片描述

  1. 獲取分佈式文件系統對象

在客戶端要使用Configuration類來加載配置文件信息,然後再調用FileSystem的get()方法,獲取一個分佈式文件系統對象DistributedFileSystem。

  1. 客戶端請求Namenode

客戶端調用DistributedFileSystem的create()方法,向Namenode發送請求新建指定路徑下的文件。客戶端採用遠程調用協議(RPC)與Namenode進行通信,這個時候Namenode要經過各種不同的檢查,如命名空間裏該路徑文件是否已存在,客戶端是否有相應權限。如果沒有通過檢查,返回IOException。如果檢查通過,Namenode就會在命名空間下新建該文件,記錄元數據(注意,此時新文件爲0字節,還沒有數據塊的信息),並返回一個FSDataOutputStream輸出流對象(dfsClient.namenode.create()在命名空間中創建一個新文件(HDFS文件),再調用DFSOutputStream的構造方法創建一個輸出流,並開啓DFSOutputStream的streamr線程。)FSDataOutputStream封裝了一個DFSDataOutputStream對象,由該對象負責處理datanode和namenode之間的通信。(DistributedFileSystem.create()會調用DFSClient.create()方法創建DFSOutputStream輸出流並構造一個HdfsDataOutputStream來包裝DFSOutputStream)
在這裏插入圖片描述

  1. 客戶端加載數據

客戶端調用DFSOutputStream的create方法,開始執行寫入操作。當寫入字節流數據達到一個數據包的長度時,DFSOutputStream就會構造一個Packet對象保存這個數據包(如果當前數據塊中的數據包都發送完畢,DFSOutputStream就會發送一個空的數據包,用來標識數據塊發送完畢),新構造的Packet會放到DFSOutputStream.dataQueue(數據隊列)中,由DataStreamer(DFSOutputStream的內部線程類)處理,向Namenode請求一組合適的Datanode列表,這一組Datanode構成一個管線。(首先調用私有的構造方法初始化一些屬性,並對shouldSyncBlock屬性賦值(是否強制關閉塊到磁盤。)然後調用computePacketChunkSize()方法設置數據包大小,同時確定一個數據包中包含校驗塊的個數,然後創建裏一個DataStreamer對象(負責建立流管道併發送數據包到數據流管道的第一個Datanode,DFSOutputStream的內部類線程)最後設置了favoredNodes字段,確認客戶端想要寫入數據塊的Datanode)

使用Packet類封裝數據包,每個數據包中都有若干個校驗塊及對應的校驗和
完整的數據包的數據分成三部分:
1.數據包包頭:記錄了數據包的概要屬性信息
2.校驗和數據
3.校驗塊數據

(DFSOutputStream.write()方法可以將指定大小的數據寫入數據流內部的一個緩衝區中,寫入的數據會被切分成多個數據包,每個數據包由一組校驗塊和對應的校驗和組成,默認的數據包大小爲65536字節(即64k,實際大小不到64k),校驗塊的大小爲512字節,每個校驗和都是校驗塊512字節數據對應的校驗值,數據包、校驗塊的大小在computePacketChunkSize()中定義。)
在這裏插入圖片描述

  1. 向Datanode中寫數據

當Datanode管線確認好之後,DataStreamer開始從dataqueue中依次取出Packet數據包,向管線中的datanode寫入。第一個Packet寫入管線中的第一個Datanode的內存中,然後第一個Datanode再將已經寫好的Packet,發送給管線中的第二個Datanode,第二個Datanode將Packet寫入內存後,再將Packet發送給第三個Datanode,然後寫到內存中。(DataStreamer在將packet發送到Datanode之前,首先在Namenode的命名空間中分配一個空的數據塊(block),建立數據塊的數據流管道。當數據流管道將當前數據塊寫滿後,會發送一個空的packet標識,然後DataStreamer會申請新的數據塊)

DataStreamer在將Packet寫入管線中時,同時也會將該Packet存儲到另外一個由ResponseProcessor線程管理的緩存隊列ackqueue中,這個隊列我們稱之爲“確認隊列”。ResponseProcessor線程會等待Datanode列表寫好的確認信息。當收到所有的Datanode的確認信息後,該線程再將ackqueue裏的packet刪除。若寫入期間發生故障,則執行以下操作:首先關閉管線,把確認隊列的所有packet都放回dataqueue的最前端,以確保故障節點後的節點不會漏掉任一packet。同時,會標識正常的Datanode,方便在故障節點恢復後,刪除存儲的部分數據塊。然後從管線中刪除故障節點,基於兩個Datanode構建一個新的管線
在這裏插入圖片描述

  1. 調用close方法

當寫入一個塊大小的n個packet數據包後,客戶端調用FSDataOutputStream的close()方法。在調用close方法前,Datanode列表將內存中的數據寫入本地磁盤。然後,DataStreamer繼續向Namenode請求下一個塊的Datanode列表,開始下一個塊的寫入。直到寫完整個文件的最後一個塊數據,然後客戶端通知Namenode,整個文件已經寫完。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章