曾經有位學者說過,“read the F**K code”。不多說,從TopicPartitionWriter的write方法中可以得到如下狀態機:
1. 狀態機中各狀態說明
WRITE_STARTED
初始狀態,無條件進入下一狀態
WRITE_PARTITION_PAUSED
如果緩衝區中有數據,將緩衝區中數據寫入hdfs tmp文件。
SHOULD_ROTATE
關閉hdfs tmp文件。
TEMP_FILE_CLOSED
數據到目標文件的準備工作,寫WAL。
WAL_APPENDED
將TMP文件轉化爲目標文件。
FILE_COMMITTED
無條件跳轉到 WRITE_PARTITION_PAUSED狀態。
2. 狀態機中條件說明
shouldRotate()
滿足兩個條件中任意一個就開始寫:
1. 時間到,可配置,例如15分鐘/60分鐘寫入一次等(非定時器實現);
2. 讀取的record數量達到配置值;
代碼詳見,下類的shouldRotate方法:
kafka-connect-hdfs-2.0.0\src\main\java\io\confluent\connect\hdfs\TopicPartitionWriter.java
3 . 附加說明
1. TMP文件
每次kafka connector從kafka中poll數據後,先將數據寫往hdfs的tmp目錄。
1. 線程爲每個kafka分區分配一個tmp文件;
2. 文件放在/topic/+tmp/分區/目錄下;
3. 文件名爲隨機產生的UUID_tmp.parquet;
4. 不是每條記錄都會直接和hdfs交互,parquet有內存緩存,寫hdfs時機可以參見:
\org\apache\parquet\hadoop\InternalParquetRecordWriter.java
2. WAL
全名”Write Ahead Log”,當異常發生時,可基於日誌進行恢復,恢復過程詳細下類的recover方法:
kafka-connect-hdfs-2.0.0\src\main\java\io\confluent\connect\hdfs\TopicPartitionWriter.java
3. 寫入目標文件
從Tmp目錄到目標目錄不會發生大規模的IO,只是做了hdfs文件的重命名,詳見下類中的commit方法
kafka-connect-hdfs-2.0.0\src\main\java\io\confluent\connect\hdfs\storage\HdfsStorage.java
4. 目標文件的隱藏地圖
目標文件的命名實際很講究,看
/topics/topic名稱/ hive分區列和值/ kafka分區+開始offset+結束offset
暗藏恢復處理流程:當新的thread接管分區的處理,或者thread從異常中恢復。會先根據該分區下最後寫入的文件名獲取最後的offset。
Kafka connect commit offset是不按套路出牌的,讀了數據有個記錄,然後定時commit。Image這種情況:數據已經寫入了目標文件,但是還沒有commit,那再讀的時候,就會重複讀數據。如果知道寫入hdfs最後的offset就不一樣了,重複的數據直接忽略掉。具體過程請參考下類的resetOffsets方法。
kafka-connect-hdfs-2.0.0\src\main\java\io\confluent\connect\hdfs\TopicPartitionWriter.java