Hadoop HDFS源碼學習筆記(六)--fetchBlockByteRange

client端需要從datanode端讀取數據,當順序的讀取block的時候,會調用到fetchBlockByteRange函數,該函數中,有一個死循環,在循環內部首先使用函數getBlockAt()獲得最新的block的信息,然後選擇要鏈接的datanode的信息得到DNAddrPair類型的對象,從該結構中得到DatanodeInfo以及datanode的InetSocketAddress,然後調用函數getBlockReader函數獲得一個BlockReader類型的對象。

 getBlockReader函數分析:

 1、首先根據dnAddr判斷是否在client端有跟該datanode的cache的連接,如果存在cache的連接,則會調用DFSClient的getLocalBlockReader函數來執行後續的操作,先不分析這類情況。

2、for循環,主要目的是允許retry連接,次數爲3

循環中:

(1)根據dnAddr去一個SocketCache類型的變量中去取得cache的Socket

如果不能從socketCache中得到cache的Socket,則會使用DFSClient的socketFactory的createSocket函數創建一個新的socket,然後執行connect連接操作

(2)調用BlockReaderFactory的newBlockReader方法new一個BlockReader類型的對象,單步跟蹤進去,發現又進行了一次封裝,facoty類函數封裝了RemoteBlockReader2的newBlockReader方法,在該方法內部,使用NetUtils的getOutputSream方法返回了一個(OutputStream)SocketOutputStream對象,利用該對象創建了一個BufferedOutputStream,然後利用該對象創建了一個DataOutputStream對象out,然後利用out創建了一個Sender對象,調用sender的readBlock方法。

Sender的readBlock方法,得到了一個OpReadBlockProto對象,並創建了該對象的header的PB的Message,然後調用自身的send函數,發送了一個ReanBlock類型的命令,在send函數中調用了DataOutputStream的Flush函數。

回到RemoteBlockReader2的newBlockReader方法:創建了一個SocketInputStream對象,然後利用該對象創建了一個DataInputStream類型的對象,之後創建了一系列的response,checksum的proto的message信息,創建的datachecksum對象,並對firstChunkOffset進行了驗證檢測。

然後調用RemoteBlockReader2的構造函數,創建了一個RemoteBlockReader2的對象,然後返回

(3)調用RemoteBlockReader2的readAll方法,該方法封裝了BlockReaderUtil的readAll方法, readAll方法,則又循環讀入,封裝的是RemoteBlockReader2的read方法,在read方法內部,首先判斷,一個java.nio.ByteBufer類型的curPacketBuf是否爲null,或者(jana.nio.ByteBuffer類型的curDataSlice的remaining爲0,且bytesNeededToFinish大於0)則會調用RemoteBlockReader2的readNextPacket()方法。在readNextPacket函數中調用了函數readPacketHeader方法,該方法最終會使用到ReadableByteChannel類型的RemoteBlockReader2的in成員變量來讀取packet的header,並將讀取過來的ByteBuffer類型的headerBuf中的packetheader字段放入到PacketHeader類型的成員變量curHeader中。

調用resetPacketBuffer函數,根據計算出來的checksumsLen和packetheader中的dataLen重設curPacketBuf以及curDataSlice的字段內容。調用readChannelFully函數從socket連接中讀取數據到curPacketBuf中。然後根據用戶請求的offset和得到的packetHeader中存儲的該packet在block中的offset計算得到應該讀取的position,然後利用該newPos對curDataSlice進行position設置。注意在radChannelFuly函數之後會根據curHeader存放的當前packet中的data的length來更新bytesNeededToFini。sh成員變量,直到該變量小於0才判定已經讀完用戶請求長度的數據,然後接受最後一個空包,然後發送給datanode一個readResult。

跳出到RemoteBlockReader2的read函數中,會發現首先需要判斷當前的packet中的dataLen和用戶請求的len的長度大小,得到小的一個值nRead,然後從curdataSlice數組中獲得nRead長度的數據。然後就跳回到BlockReaderUtil的readAll函數中,該函數會根據當前的nRead值以及之前的累積讀到的數據長度和用戶請求的len來判定是否繼續執行read操作。

至此就使用BlockReader讀取了用戶請求的數據,然後會關閉當前的BlockReader,並返回,到目前就完成的fecthBlockByteRange的函數操作,從之前的分析可以瞭解到,該函數執行的操作是根據block的元數據信息從一個datanode上讀取block中的數據

現將在該追蹤過程中畫的序列圖和相關類圖附上,以便理清思路,源碼就不貼了,因爲這次用的是23的代碼,本機上沒有,是在臺式機上追蹤的,兩臺電腦沒互通呵呵。





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