Spark結構化流編程指南【基礎信息】

一.概述

結構化流是基於Spark SQL引擎構建的可伸縮且容錯的流處理引擎。可以像對靜態數據進行批處理計算一樣來表示流計算。當流數據繼續到達時,Spark SQL引擎將負責逐步遞增地運行它並更新最終結果。可以在Scala,Java,Python或R中使用Dataset / DataFrame API來表示流聚合,事件時間窗口,流到批處理聯接等。計算在同一優化的Spark SQL引擎上執行。最後,系統通過檢查點和預寫日誌來確保端到端的一次容錯保證。簡而言之,結構化流提供了快速,可擴展,容錯,端到端的精確一次流處理,而用戶無需推理流。

在內部,默認情況下,結構化流查詢是使用微批量處理引擎處理的,該引擎將數據流作爲一系列小批量作業處理,從而實現了低至100毫秒的端到端延遲以及一次精確的容錯保證。但是,自Spark 2.3起,我們引入了一種稱爲“ 連續處理”的新低延遲處理模式,該模式可以實現一次最少保證的低至1毫秒的端到端延遲。在不更改查詢中的Dataset / DataFrame操作的情況下,將能夠根據應用程序需求選擇模式。

二.快速案例

1.獲取SparkSession入口並導入轉換

    val spark = SparkSession
      .builder()
      .appName("從Socket獲取數據進行結構化處理")
      .master("local[2]")
      .getOrCreate()

    // 導入轉換
    import spark.implicits._

2.創建流處理框架

    // split the lines into words
    val wordCounts = lines.as[String]
      .flatMap(_.split(" "))
      .groupBy("value")
      .count()

此linesDataFrame表示一個包含流文本數據的無界表。該表包含一列名爲“值”的字符串,流文本數據中的每一行都成爲表中的一行。請注意,由於我們正在設置轉換,並且尚未開始轉換,因此當前未接收到任何數據。接下來,我們使用將該DataFrame轉換爲String的Dataset .as[String],以便我們可以應用該flatMap操作將每一行拆分爲多個單詞。最後,我們wordCounts通過對數據集中的唯一值進行分組並對其進行計數來定義DataFrame。請注意,這是一個流數據幀,它表示流的運行字數。
3.開啓執行
設置outputMode(“complete”)爲在每次更新計數時將完整的計數集(由指定)打印到控制檯。然後使用開始流計算start()。

    // start running program
    val result = wordCounts
      .writeStream
      .outputMode("complete")
      .format("console")
      .start()
    
    result.awaitTermination()

執行此代碼後,流計算將在後臺開始。result對象是該活動流查詢的句柄,我們已決定使用來等待查詢終止,awaitTermination()以防止在該查詢處於活動狀態時退出該過程。
4.啓動Socket
啓動linux系統,執行:nc -lk 9999,啓動Socket
在這裏插入圖片描述
5.輸入數據
在這裏插入圖片描述
6.執行結果

-------------------------------------------
Batch: 0
-------------------------------------------
+-----+-----+
|value|count|
+-----+-----+
+-----+-----+

-------------------------------------------
Batch: 1
-------------------------------------------
+-----+-----+
|value|count|
+-----+-----+
|scala|    1|
+-----+-----+

-------------------------------------------
Batch: 2
-------------------------------------------
+------+-----+
| value|count|
+------+-----+
| scala|    1|
| spark|    2|
|  sale|    1|
| flink|    1|
|  scla|    1|
|hadoop|    2|
+------+-----+

-------------------------------------------
Batch: 3
-------------------------------------------
+------+-----+
| value|count|
+------+-----+
| scala|    1|
| spark|    3|
|  sale|    1|
| flink|    1|
|  scla|    1|
|hadoop|    2|
+------+-----+

7.備註

  • 若先啓動程序則首次執行內容爲空!
  • 程序執行的觸發的時間差異較大,需耐心等待!

三.編程模型

結構化流傳輸中的關鍵思想是將實時數據流視爲被連續添加的表。這導致了一個新的流處理模型,該模型與批處理模型非常相似。將像在靜態表上一樣將流計算表示爲類似於批處理的標準查詢,Spark 在無界輸入表上將其作爲增量查詢運行。
1.基本概念
將輸入數據流視爲“輸入表”。流上到達的每個數據項都像是將新行附加到輸入表中。
圖解如下:
在這裏插入圖片描述
對輸入的查詢將生成“結果表”。在每個觸發間隔(例如,每1秒鐘),新行將附加到輸入表中,並最終更新結果表。無論何時更新結果表,我們都希望將更改後的結果行寫入外部接收器。
在這裏插入圖片描述
“輸出”定義爲寫到外部存儲器的內容。可以在不同的模式下定義輸出:

  • complete完整模式 -整個更新的結果表將被寫入外部存儲器。由存儲連接器決定如何處理整個表的寫入。
  • append追加模式 -僅將自上次觸發以來追加在結果表中的新行寫入外部存儲器。這僅適用於結果表中現有行預計不會更改的查詢。
  • update更新模式 -僅自上次觸發以來在結果表中已更新的行將被寫入外部存儲(自Spark 2.1.1起可用)。請注意,這與完整模式的不同之處在於此模式僅輸出自上次觸發以來已更改的行。如果查詢不包含聚合,它將等同於追加模式。

請注意,每種模式都適用於某些類型的查詢。稍後將對此進行詳細討論。

爲了說明此模型的用法,讓我們在上面的快速示例的上下文中瞭解該模型。第一個linesDataFrame是輸入表,最後一個wordCountsDataFrame是結果表。需要注意的是在流處理的查詢lines數據幀生成wordCounts是完全一樣的,因爲它是一個靜態的數據幀。但是,啓動此查詢後,Spark將不斷檢查套接字連接中是否有新數據。如果有新數據,Spark將運行一個“增量”查詢,該查詢將先前的運行計數與新數據結合起來以計算更新的計數,如下所示。
在這裏插入圖片描述
請注意,結構化流不會實現整個表。它從流數據源讀取最新的可用數據,對其進行增量處理以更新結果,然後丟棄該源數據。它僅保留更新結果所需的最小中間狀態數據(例如,前面示例中的中間計數)。

此模型與許多其他流處理引擎明顯不同。許多流系統要求用戶自己維護運行中的聚合,因此必須考慮容錯和數據一致性(至少一次,最多一次或恰好一次)。在此模型中,Spark負責在有新數據時更新結果表,從而使用戶免於推理。
2.處理事件時間和延遲數據
事件時間是嵌入數據本身的時間。對於許多應用程序,可能需要在此事件時間進行操作。例如,如果要獲取每分鐘由IoT設備生成的事件數,則可能要使用生成數據的時間(即數據中的事件時間),而不是Spark收到的時間。此事件時間在此模型中非常自然地表示-設備中的每個事件都是表中的一行,而事件時間是該行中的列值。這允許基於窗口的聚合(例如,每分鐘的事件數)只是事件時間列上的一種特殊的分組和聚合類型-每個時間窗口是一個組,每行可以屬於多個窗口/組。

此外,此模型自然會根據事件時間處理比預期晚到達的數據。由於Spark正在更新結果表,因此它具有完全控制權,可以在有較晚數據時更新舊聚合,並可以清除舊聚合以限制中間狀態數據的大小。從Spark 2.1開始支持水印功能,該功能允許用戶指定最新數據的閾值,並允許引擎相應地清除舊狀態。
3.容錯語義
提供端到端的一次語義是結構化流設計背後的主要目標之一。爲此,我們設計了結構化流源,接收器和執行引擎,以可靠地跟蹤處理的確切進度,以便它可以通過重新啓動和/或重新處理來處理任何類型的故障。假定每個流源都有偏移量(類似於Kafka偏移量或Kinesis序列號),以跟蹤流中的讀取位置。引擎使用檢查點和預寫日誌來記錄每個觸發器中正在處理的數據的偏移範圍。流接收器被設計爲是冪等的,用於處理流程。結合使用可重播的源和冪等的接收器,結構化流可以確保在任何故障下端到端的一次精確語義 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章