Spark學習筆記(三)-Spark Streaming

薩姆大叔發表於2020-06-24

Spark Streaming支援實時資料流的可擴充套件(scalable)、高吞吐(high-throughput)、容錯(fault-tolerant)的流處理(stream processing)。

 

                                                    架構圖

 

特性如下:

 

  • 可線性伸縮至超過數百個節點;

  • 實現亞秒級延遲處理;

  • 可與Spark批處理和互動式處理無縫整合;

  • 提供簡單的API實現複雜演算法;

  • 更多的流方式支援,包括Kafka、Flume、Kinesis、Twitter、ZeroMQ等。

 

原理

 

Spark在接收到實時輸入資料流後,將資料劃分成批次(divides the data into batches),然後轉給Spark Engine處理,按批次生成最後的結果流(generate the final stream of results in batches)。 

 

 

API

 

DStream

 

DStream(Discretized Stream,離散流)是Spark Stream提供的高階抽象連續資料流。

 

  • 組成:一個DStream可看作一個RDDs序列。

  • 核心思想:將計算作為一系列較小時間間隔的、狀態無關的、確定批次的任務,每個時間間隔內接收的輸入資料被可靠儲存在叢集中,作為一個輸入資料集。

 

 

  • 特性:一個高層次的函數語言程式設計API、強一致性以及高校的故障恢復。

  • 應用程式模板:

  • 模板1

  • 模板2

 

WordCount示例

 

 

Input DStream

 

Input DStream是一種從流式資料來源獲取原始資料流的DStream,分為基本輸入源(檔案系統、Socket、Akka Actor、自定義資料來源)和高階輸入源(Kafka、Flume等)。

 

  • Receiver:
  • 每個Input DStream(檔案流除外)都會對應一個單一的Receiver物件,負責從資料來源接收資料並存入Spark記憶體進行處理。應用程式中可建立多個Input DStream並行接收多個資料流。

  • 每個Receiver是一個長期執行在Worker或者Executor上的Task,所以會佔用該應用程式的一個核(core)。如果分配給Spark Streaming應用程式的核數小於或等於Input DStream個數(即Receiver個數),則只能接收資料,卻沒有能力全部處理(檔案流除外,因為無需Receiver)。

  • Spark Streaming已封裝各種資料來源,需要時參考官方文件。

 

Transformation Operation

 

  • 常用Transformation

 

* map(func) :對源DStream的每個元素,採用func函式進行轉換,得到一個新的DStream;

* flatMap(func):與map相似,但是每個輸入項可用被對映為0個或者多個輸出項;

* filter(func):返回一個新的DStream,僅包含源DStream中滿足函式func的項;

* repartition(numPartitions):通過建立更多或者更少的分割槽改變DStream的並行程度;

* union(otherStream):返回一個新的DStream,包含源DStream和其他DStream的元素;

* count():統計源DStream中每個RDD的元素數量;

* reduce(func):利用函式func聚集源DStream中每個RDD的元素,返回一個包含單元素RDDs的新DStream;

* countByValue():應用於元素型別為K的DStream上,返回一個(K,V)鍵值對型別的新DStream,每個鍵的值是在原DStream的每個RDD中的出現次數;

* reduceByKey(func, [numTasks]):當在一個由(K,V)鍵值對組成的DStream上執行該操作時,返回一個新的由(K,V)鍵值對組成的DStream,每一個key的值均由給定的recuce函式(func)聚集起來;

* join(otherStream, [numTasks]):當應用於兩個DStream(一個包含(K,V)鍵值對,一個包含(K,W)鍵值對),返回一個包含(K, (V, W))鍵值對的新DStream;

* cogroup(otherStream, [numTasks]):當應用於兩個DStream(一個包含(K,V)鍵值對,一個包含(K,W)鍵值對),返回一個包含(K, Seq[V], Seq[W])的元組;

* transform(func):通過對源DStream的每個RDD應用RDD-to-RDD函式,建立一個新的DStream。支援在新的DStream中做任何RDD操作。

 

  • updateStateByKey(func)

  • updateStateByKey可對DStream中的資料按key做reduce,然後對各批次資料累加

  • WordCount的updateStateByKey版本

 

  • transform(func)

  • 通過對原DStream的每個RDD應用轉換函式,建立一個新的DStream。

  • 官方文件程式碼舉例

 

  • Window operations

  • 視窗操作:基於window對資料transformation(個人認為與Storm的tick相似,但功能更強大)。

  • 引數:視窗長度(window length)和滑動時間間隔(slide interval)必須是源DStream批次間隔的倍數。

  • 舉例說明:視窗長度為3,滑動時間間隔為2;上一行是原始DStream,下一行是視窗化的DStream。

  • 常見window operation

有狀態轉換包括基於滑動視窗的轉換和追蹤狀態變化(updateStateByKey)的轉換。

基於滑動視窗的轉換

* window(windowLength, slideInterval) 基於源DStream產生的視窗化的批資料,計算得到一個新的DStream;

* countByWindow(windowLength, slideInterval) 返回流中元素的一個滑動視窗數;

* reduceByWindow(func, windowLength, slideInterval) 返回一個單元素流。利用函式func聚集滑動時間間隔的流的元素建立這個單元素流。函式func必須滿足結合律,從而可以支援平行計算;

* reduceByKeyAndWindow(func, windowLength, slideInterval, [numTasks]) 應用到一個(K,V)鍵值對組成的DStream上時,會返回一個由(K,V)鍵值對組成的新的DStream。每一個key的值均由給定的reduce函式(func函式)進行聚合計算。注意:在預設情況下,這個運算元利用了Spark預設的併發任務數去分組。可以通過numTasks引數的設定來指定不同的任務數;

* reduceByKeyAndWindow(func, invFunc, windowLength, slideInterval, [numTasks]) 更加高效的reduceByKeyAndWindow,每個視窗的reduce值,是基於先前視窗的reduce值進行增量計算得到的;它會對進入滑動視窗的新資料進行reduce操作,並對離開視窗的老資料進行“逆向reduce”操作。但是,只能用於“可逆reduce函式”,即那些reduce函式都有一個對應的“逆向reduce函式”(以InvFunc引數傳入);

* countByValueAndWindow(windowLength, slideInterval, [numTasks]) 當應用到一個(K,V)鍵值對組成的DStream上,返回一個由(K,V)鍵值對組成的新的DStream。每個key的值都是它們在滑動視窗中出現的頻率。

  • 官方文件程式碼舉例 

 

  • join(otherStream, [numTasks])

  • 連線資料流

  • 官方文件程式碼舉例1

  • 官方文件程式碼舉例2

 

Output Operation

 

 

快取與持久化

 

  • 通過persist()將DStream中每個RDD儲存在記憶體。

  • Window operations會自動持久化在記憶體,無需顯示呼叫persist()。

  • 通過網路接收的資料流(如Kafka、Flume、Socket、ZeroMQ、RocketMQ等)執行persist()時,預設在兩個節點上持久化序列化後的資料,實現容錯。

 

Checkpoint

 

  • 用途:Spark基於容錯儲存系統(如HDFS、S3)進行故障恢復。

  • 分類:

  • 後設資料檢查點:儲存流式計算資訊用於Driver執行節點的故障恢復,包括建立應用程式的配置、應用程式定義的DStream operations、已入隊但未完成的批次。

  • 資料檢查點:儲存生成的RDD。由於stateful transformation需要合併多個批次的資料,即生成的RDD依賴於前幾個批次RDD的資料(dependency chain),為縮短dependency chain從而減少故障恢復時間,需將中間RDD定期儲存至可靠儲存(如HDFS)。

  • 使用時機:

  • Stateful transformation:updateStateByKey()以及window operations。

  • 需要Driver故障恢復的應用程式。

  • 使用方法

  • Stateful transformation

streamingContext.checkpoint(checkpointDirectory)

 

  • 需要Driver故障恢復的應用程式(以WordCount舉例):如果checkpoint目錄存在,則根據checkpoint資料建立新StreamingContext;否則(如首次執行)新建StreamingContext。

 

  • checkpoint時間間隔

  • 方法:

dstream.checkpoint(checkpointInterval)

 

  • 原則:一般設定為滑動時間間隔的5-10倍。

  • 分析:checkpoint會增加儲存開銷、增加批次處理時間。當批次間隔較小(如1秒)時,checkpoint可能會減小operation吞吐量;反之,checkpoint時間間隔較大會導致lineage和task數量增長。

 

效能調優

 

降低批次處理時間

 

  • 資料接收並行度

  • 增加DStream:接收網路資料(如Kafka、Flume、Socket等)時會對資料反序列化再儲存在Spark,由於一個DStream只有Receiver物件,如果成為瓶頸可考慮增加DStream。

  • 設定“spark.streaming.blockInterval”引數:接收的資料被儲存在Spark記憶體前,會被合併成block,而block數量決定了Task數量;舉例,當批次時間間隔為2秒且block時間間隔為200毫秒時,Task數量約為10;如果Task數量過低,則浪費了CPU資源;推薦的最小block時間間隔為50毫秒。

  • 顯式對Input DStream重新分割槽:在進行更深層次處理前,先對輸入資料重新分割槽。

inputStream.repartition(<number of partitions>)

 

  • 資料處理並行度:reduceByKey、reduceByKeyAndWindow等operation可通過設定“spark.default.parallelism”引數或顯式設定並行度方法引數控制。

  • 資料序列化:可配置更高效的Kryo序列化。

 

設定合理批次時間間隔

 

  • 原則:處理資料的速度應大於或等於資料輸入的速度,即批次處理時間大於或等於批次時間間隔。

  • 方法:

  • 先設定批次時間間隔為5-10秒以降低資料輸入速度;

  • 再通過檢視log4j日誌中的“Total delay”,逐步調整批次時間間隔,保證“Total delay”小於批次時間間隔。

 

記憶體調優

 

  • 持久化級別:開啟壓縮,設定引數“spark.rdd.compress”。

  • GC策略:在Driver和Executor上開啟CMS。

相關文章