天天看點

HDFS源碼分析EditLog之讀取操作符

        4、從編輯日志editLog中擷取編輯日志輸入流集合streams,擷取的輸入流為最新事務ID加1之後的資料

        5、調用檔案系統鏡像FSImage執行個體image的loadEdits(),利用編輯日志輸入流集合streams,加載編輯日志至目标namesystem中的檔案系統鏡像FSImage,并獲得編輯日志加載的大小editsLoaded;

        可見,我們在獲得編輯日志輸入流EditLogInputStream的集合streams後,就需要調用FSImage的loadEdits()方法,利用編輯日志輸入流集合streams,加載編輯日志至目标namesystem中的檔案系統鏡像FSImage。而HDFS是如何從編輯日志輸入流中讀取資料的呢?本文,我們将進行詳細的探究!

        首先,在加載編輯日志的主要類FSEditLogLoader中,其核心方法loadEditRecords()中有如下一段代碼:

        它會從編輯日志輸入流in中讀取一個操作符op,然後調用applyEditLogOp()方法,将操作符作用于記憶體中繼資料FSNamesystem。那麼問題來了,這個操作符如何從資料流中被讀取并解析的呢?

        接下來,我們就看下如何從編輯日志輸出流EditLogInputStream中讀取一個操作符,我們先看其readOp()方法,代碼如下:

        很簡單,如果緩存的cachedOp不為null,傳回緩存的cachedOp,并将其清空,如果緩存的cachedOp為null,則調用nextOp()進行處理。而EditLogInputStream中nextOp()是一個抽象方法,我們需要看其子類的實作方法,下面就以EditLogFileInputStream為例,看下其nextOp()方法:

        繼續追蹤nextOpImpl()方法,代碼如下:

        nextOpImpl()方法的大體處理邏輯如下:

        根據編輯日志檔案輸入流的狀态判斷:

        1、如果為未初始化狀态UNINIT,調用init()方法進行初始化,然後檢測編輯日志檔案輸入流狀态,此時不應為UNINIT,最後再次調用nextOpImpl()方法;

        2、如果為打開OPEN狀态,調用FSEditLogOp.Reader的readOp()方法,讀取操作符op;

        3、如果為關閉CLOSED狀态,直接傳回null。

        我們重點關注下FSEditLogOp.Reader的readOp()方法,代碼如下:

        繼續追蹤decodeOp()方法,代碼如下:

        decodeOp()方法的邏輯很簡單:

        1、從輸入流in中讀取一個byte,即opCodeByte,确定操作類型;

        2、将byte類型的opCodeByte轉換成FSEditLogOpCodes對象opCode;

        3、根據FSEditLogOpCodes對象opCode從cache中擷取FSEditLogOp對象op,這樣我們就得到了操作符對象;

        4、如果支援編輯日志長度,從輸入流讀入一個int;

        5、如果支援事務ID,讀入一個long,作為事務ID,并在FSEditLogOp執行個體op中設定事務ID,否則在FSEditLogOp執行個體op中設定事務ID為-12345;

        6、調用操作符對象op的readFields()方法,從輸入流in中讀入其他域,并設定入FSEditLogOp執行個體op。

        接下來,我們再看下操作符對象的readFields()方法,因為不同種類的操作符肯定包含不同的屬性,是以它們的readFields()方法肯定也各不相同。下面,我們就以操作符AddCloseOp為例來分析,其readFields()方法如下:

        這個沒有什麼特别好講的,依次讀入操作符需要的,在輸入流中依次存在的屬性即可。

        不過,我們仍然需要重點講解下讀入資料塊的readBlocks()方法,代碼如下:

        很簡單,先從輸入流讀取block數目numBlocks,确定一共需要讀取多少個資料塊,然後構造block數組blocks,大小即為numBlocks,最後從輸入流中讀取numBlocks個資料塊,每次都是先構造資料塊Block執行個體blk,調用Block的readFields()方法,從輸入流讀入資料塊,然後将資料塊blk放入資料塊數組blocks。全部資料塊讀取完畢後,傳回資料塊數組blocks。

        我們再看下資料塊Block的readFields()方法,如下:

        繼續看readHelper()方法,如下:

        從輸入流依次讀入資料塊艾迪blockId、資料塊大小numBytes、資料塊産生的時間戳generationStamp即可,三者均為long類型。

        總結

繼續閱讀