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。