一個 Streaming Application 往往需要7*24不間斷的跑,所以需要有抵御意外的能力(比如機器或者系統掛掉,JVM crash等)。為了讓這成為可能,Spark Streaming需要 checkpoint 足夠多信息至一個具有容錯設計的存儲系統才能讓 Application 從失敗中恢復。Spark Streaming 會 checkpoint 兩種類型的數據。
- Metadata(元數據) checkpointing - 保存定義了 Streaming 計算邏輯至類似 HDFS 的支持容錯的存儲系統。用來恢復 driver,元數據包括:
- 配置 - 用于創建該 streaming application 的所有配置
- DStream 操作 - DStream 一些列的操作
- 未完成的 batches - 那些提交了 job 但尚未執行或未完成的 batches
- Data checkpointing - 保存已生成的RDDs至可靠的存儲。這在某些 stateful 轉換中是需要的,在這種轉換中,生成 RDD 需要依賴前面的 batches,會導致依賴鏈隨著時間而變長。為了避免這種沒有盡頭的變長,要定期將中間生成的 RDDs 保存到可靠存儲來切斷依賴鏈
總之,metadata checkpointing 主要用來恢復 driver;而 RDD數據的 checkpointing 對于stateful 轉換操作是必要的。
什么時候需要啟用 checkpoint?
什么時候該啟用 checkpoint 呢?滿足以下任一條件:
- 使用了 stateful 轉換 - 如果 application 中使用了
updateStateByKey
或reduceByKeyAndWindow
等 stateful 操作,必須提供 checkpoint 目錄來允許定時的 RDD checkpoint - 希望能從意外中恢復 driver
如果 streaming app 沒有 stateful 操作,也允許 driver 掛掉后再次重啟的進度丟失,就沒有啟用 checkpoint的必要了。
如何使用 checkpoint?
啟用 checkpoint,需要設置一個支持容錯 的、可靠的文件系統(如 HDFS、s3 等)目錄來保存 checkpoint 數據。通過調用 streamingContext.checkpoint(checkpointDirectory)
來完成。另外,如果你想讓你的 application 能從 driver 失敗中恢復,你的 application 要滿足:
- 若 application 為首次重啟,將創建一個新的 StreamContext 實例
- 如果 application 是從失敗中重啟,將會從 checkpoint 目錄導入 checkpoint 數據來重新創建 StreamingContext 實例
通過 StreamingContext.getOrCreate
可以達到目的:
// Function to create and setup a new StreamingContext
def functionToCreateContext(): StreamingContext = {
val ssc = new StreamingContext(...) // new context
val lines = ssc.socketTextStream(...) // create DStreams
...
ssc.checkpoint(checkpointDirectory) // set checkpoint directory
ssc
}
// Get StreamingContext from checkpoint data or create a new one
val context = StreamingContext.getOrCreate(checkpointDirectory, functionToCreateContext _)
// Do additional setup on context that needs to be done,
// irrespective of whether it is being started or restarted
context. ...
// Start the context
context.start()
context.awaitTermination()
如果 checkpointDirectory 存在,那么 context 將導入 checkpoint 數據。如果目錄不存在,函數 functionToCreateContext 將被調用并創建新的 context
除調用 getOrCreate 外,還需要你的集群模式支持 driver 掛掉之后重啟之。例如,在 yarn 模式下,driver 是運行在 ApplicationMaster 中,若 ApplicationMaster 掛掉,yarn 會自動在另一個節點上啟動一個新的 ApplicationMaster。
需要注意的是,隨著 streaming application 的持續運行,checkpoint 數據占用的存儲空間會不斷變大。因此,需要小心設置checkpoint 的時間間隔。設置得越小,checkpoint 次數會越多,占用空間會越大;如果設置越大,會導致恢復時丟失的數據和進度越多。一般推薦設置為 batch duration 的5~10倍。
導出 checkpoint 數據
上文提到,checkpoint 數據會定時導出到可靠的存儲系統,那么
- 在什么時機進行 checkpoint
- checkpoint 的形式是怎么樣的
checkpoint 的時機
在 Spark Streaming 中,JobGenerator 用于生成每個 batch 對應的 jobs,它有一個定時器,定時器的周期即初始化 StreamingContext 時設置的 batchDuration。這個周期一到,JobGenerator 將調用generateJobs方法來生成并提交 jobs,這之后調用 doCheckpoint 方法來進行 checkpoint。doCheckpoint 方法中,會判斷當前時間與 streaming application start 的時間之差是否是 checkpoint duration 的倍數,只有在是的情況下才進行 checkpoint。
checkpoint 的形式
最終 checkpoint 的形式是將類 Checkpoint的實例序列化后寫入外部存儲,值得一提的是,有專門的一條線程來做將序列化后的 checkpoint 寫入外部存儲。類 Checkpoint 包含以下數據
除了 Checkpoint 類,還有 CheckpointWriter 類用來導出 checkpoint,CheckpointReader 用來導入 checkpoint
Checkpoint 的局限
Spark Streaming 的 checkpoint 機制看起來很美好,卻有一個硬傷。上文提到最終刷到外部存儲的是類 Checkpoint 對象序列化后的數據。那么在 Spark Streaming application 重新編譯后,再去反序列化 checkpoint 數據就會失敗。這個時候就必須新建 StreamingContext。
針對這種情況,在我們結合 Spark Streaming + kafka 的應用中,我們自行維護了消費的 offsets,這樣一來及時重新編譯 application,還是可以從需要的 offsets 來消費數據,這里只是舉個例子,不詳細展開了。