Spark Streaming 的容錯機制

說出你的願望吧發表於2020-03-01

前言

···

一、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個

  1. 成功task的數量 > 0.75 * 10,0.75的對應引數為,

    spark.speculation.quantile=0.75

0.75是此引數的預設值

  1. 正在執行的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 語義

  1. At most once 一條記錄要麼被處理一次,要麼沒有被處理,資料可能丟

  2. At least once 一條記錄可能被處理一次或者多次,可能會重複處理

  3. Exactly once 一條記錄只被處理一次,這是要求最高的

finally

···

相關文章