前言
···
一、Spark Streaming的容錯
此時我們啟動一個Application任務,根據我們啟動的模式和執行叢集的型別,會根據一定的策略選擇一臺伺服器當做Driver伺服器,在其初始化完成之後,就會順帶把這些Executor給初始化完成。
之後Driver就會傳送Receiver到某一個Executor上面,Receiver就是負責用來接收資料的(你也可以當成是一個task任務)。它每隔200毫秒就會將自己接收上來的資料儲存成一個block,而且這個block肯定就會有副本機制。
當block塊完成生成,就會給Driver返回報告資訊,讓它得知block們的存在,之後Driver會生成一個個小的任務分發給這些Executor完成。
1.1 Executor掛掉
那此時我們的Executor掛掉怎麼辦呢?
首先我們圖中下方的Executor掛掉,那是完全不礙事的,因為所有的資料都在上面的Executor中,可是當上方的Executor也掛掉的話,它的Receiver自然就無法正常工作了,那資料就會丟失了。
你以為我會這麼說嗎??其實不然,因為Spark Streaming自身就存在很好的容錯機制,當存在有Receiver的Executor掛掉之後Driver會自動又尋找一個Executor把Receiver給再建立回來,因為資料都是有備份block的,所以也不太需要擔心資料丟失的問題
而那些本身分發給掛掉的Executor的任務也會重新分發出來重新執行,這一點是不需要人工干預的。
1.2 Driver掛掉
那如果Driver掛掉,那所有的Executor也會失敗,這時我們就需要使用checkPoint機制,它會定期的將Driver資訊寫入到HDFS中。
根據這個思路,我們想達到的目標是,讓Driver掛掉時能夠自動恢復,接著上一次的結果進行計算,而不需要人為干預
1.2.1 設定自動重啟Driver
此時假設我們是在叢集模式下,因為只有在叢集模式下才能實現容錯。
Standalone
在spark-submit中增加以下兩個引數:
--deploy-mode cluster
--supervise(這個就是讓任務自動重啟的引數)
複製程式碼
Yarn(大多數的情況)
在spark-submit中增加以下引數:
--deploy-mode cluster
複製程式碼
在yarn配置中設定 yarn.resourcemanager.am.max-attemps,這個是設定一個任務最多可失敗多少次。假設這個引數配置為3(我們公司就是3次?),那我們的任務掛掉的時候,Yarn會自動幫我們重啟,第三次後再掛掉,那就真的掛了。
Mesos
Marathon 可以重啟 Mesos應用
1.2.2 設定HDFS的checkPoint目錄
指定一個checkPoint目錄儲存你的關鍵資訊,不然就無法恢復了。一般我們設定的目錄就是HDFS目錄,本身HDFS就有高可用的特性,沒有單點故障問題。
就一行程式碼,沒啥特別的
streamingContext.setCheckpoint(hdfsDirectory)
複製程式碼
1.2.3 code
// 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()
複製程式碼
程式碼邏輯其實就是getOrCreate,如果checkpointDirectory存在資料,那就恢復,如果不存在資料,那就自己建立一個Driver
1.3 Executor的資料丟失問題
此時我們可以把Driver給恢復回來,可是Driver當機導致Executor的記憶體中存在的資料也一併丟失掉了,所以我們也得對這部分資料進行一些容錯機制
WAL機制就是:比如我們現在Kafka通過Data Stream傳送資料過來了,Receiver接收後會開啟WAL機制,就會將資料寫入到HDFS裡面,待寫入完成後Receiver會通知Kafka告知資料已經成功接收到了(此時Kafka就是acks設定為-1了),這個做法其實就是和checkPoint是一模一樣的,只是換了個WAL的名號
1.3.1 設定checkpoint目錄
streamingContext.setCheckpoint(hdfsDirectory)
複製程式碼
這個就不用展開了吧,剛剛才說完的東西
1.3.2 開啟WAL日誌
它預設是不開啟的,所以要手動設定為true
sparkConf.set(“spark.streaming.receiver.writeAheadLog.enable”, “true”)
複製程式碼
1.3.3 reliable receiver
這裡不能被誤導,其實在這裡說的是可靠的資料來源問題。這個可靠的資料來源就是指Kafka,當資料寫完了WAL後,才告訴Kafka資料已經消費,對於沒有反饋給Kafka的資料,可以從Kafka中重新消費資料
這話好像有點繞口,其實就是這樣:Receiver要往HDFS上去寫入資料,這時如果程式掛了,根本就沒來得及去寫,那此時我們就會再次從Kafka中去重新獲取,那Kafka確實就是能夠支援資料的回溯,再傳送之前傳送過的資料給Receiver(Kafka系列的文章中已經寫過了)。
那什麼才算是不可靠的資料來源呢,比如Socket就是,它並不能保留之前的資料,丟了就是丟了,無法再次從它那裡獲取。
1.3.4 取消備份
使用StorageLevel.MEMORY_AND_DISK_SER來儲存資料來源,不需要字尾為2的策略了(預設是字尾為2的策略),因為HDFS已經是多副本了。
1.4 解決某一個task執行很慢的問題
我們需要開啟推測機制:
spark.speculation=true,
複製程式碼
這個機制開啟後每隔一段時間來檢查有哪些正在執行的task需要重新排程,時間間隔設定如下
spark.speculation.interval=100ms
複製程式碼
此時我們如何判斷一個task需要重新排程呢?有兩個需要達到的條件。此時我們假設總的task有10個
成功task的數量 > 0.75 * 10,0.75的對應引數為,
spark.speculation.quantile=0.75
0.75是此引數的預設值
正在執行的task的執行時間 > 1.5 * 成功 執行task的 平均 時間,1.5的對應引數為,
spark.speculation.multiplier=1.5
1.5是此引數的預設值
滿足上述兩個條件,則這個正在執行的task需要重新等待排程。如果這個task的緩慢是因為資料傾斜問題,那就參照 一文帶你理清Spark Core調優的方方面面 中提到的思路解決
假如我們開啟了推測執行恰好又遇上了資料傾斜,會有機率遇上一個非常尷尬的情況,就是資料傾斜執行時間的那個task假設要10秒,此時我還剩那麼1~2秒就該處理完了,此時滿足了上面兩個條件,這個task被分發,然後再次執行,然後又傾斜,再次分發···,死迴圈一通下來這個task怎麼都處理不完。
所以推測執行使用前是要慎重的,它能解決full gc或者網路抖動的情況,可是資料傾斜,它的存在就會成為瓶頸。
1.5 Spark Streaming 語義
At most once 一條記錄要麼被處理一次,要麼沒有被處理,資料可能丟
At least once 一條記錄可能被處理一次或者多次,可能會重複處理
Exactly once 一條記錄只被處理一次,這是要求最高的
finally
···