創(chuàng)建 streaming DataFrames 和 streaming Datasets
streaming DataFrames/Datasets 的模式接口和分區(qū)
streaming DataFrames/Datasets 上的操作
基礎(chǔ)操作 - Selection, Projection, Aggregation
Window Operations on Event Time (事件時(shí)間窗口操作)
處理 Late Data (遲到數(shù)據(jù))和 Watermarking (水印)
Streaming Deduplication (Streaming 去重)
Arbitrary Stateful Operations (任意有狀態(tài)的操作)
Recovering from Failures with Checkpointing (從檢查點(diǎn)恢復(fù)故障)
Structured Streaming (結(jié)構(gòu)化流)是一種基于 Spark SQL 引擎構(gòu)建的可擴(kuò)展且容錯(cuò)的 stream processing engine (流處理引擎)。您可以以靜態(tài)數(shù)據(jù)表示批量計(jì)算的方式來表達(dá) streaming computation (流式計(jì)算)。 Spark SQL 引擎將隨著 streaming data 持續(xù)到達(dá)而增量地持續(xù)地運(yùn)行,并更新最終結(jié)果。您可以使用 Scala , Java , Python 或 R 中的Dataset/DataFrame API來表示 streaming aggregations (流聚合), event-time windows (事件時(shí)間窗口), stream-to-batch joins (流到批處理連接) 等。在同一個(gè) optimized Spark SQL engine (優(yōu)化的 Spark SQL 引擎)上執(zhí)行計(jì)算。最后,系統(tǒng)通過 checkpointing (檢查點(diǎn)) 和 Write Ahead Logs (預(yù)寫日志)來確保 end-to-end exactly-once (端到端的完全一次性) 容錯(cuò)保證。簡(jiǎn)而言之,Structured Streaming 提供快速,可擴(kuò)展,容錯(cuò),end-to-end exactly-once stream processing (端到端的完全一次性流處理),而無需用戶理解 streaming 。
在本指南中,我們將向您介紹 programming model (編程模型) 和 APIs 。首先,我們從一個(gè)簡(jiǎn)單的例子開始 - 一個(gè) streaming word count 。
假設(shè)您想要保持從監(jiān)聽 TCP socket 的 data server (數(shù)據(jù)服務(wù)器) 接收的 text data (文本數(shù)據(jù))的運(yùn)行的 word count 。 讓我們看看如何使用 Structured Streaming 表達(dá)這一點(diǎn)。你可以在Scala/Java/Python/R之中看到完整的代碼。 Let’s say you want to maintain a running word count of text data received from a data server listening on a TCP socket. Let’s see how you can express this using Structured Streaming. You can see the full code inScala/Java/Python/R。 并且如果您下載 Spark,您可以直接運(yùn)行這個(gè)例子。在任何情況下,讓我們逐步了解示例并了解它的工作原理。首先,我們必須導(dǎo)入必要的 classes 并創(chuàng)建一個(gè)本地的 SparkSession ,這是與 Spark 相關(guān)的所有功能的起點(diǎn)。
importorg.apache.spark.sql.functions._importorg.apache.spark.sql.SparkSessionvalspark=SparkSession.builder.appName("StructuredNetworkWordCount").getOrCreate()importspark.implicits._
接下來,我們創(chuàng)建一個(gè) streaming DataFrame ,它表示從監(jiān)聽 localhost:9999 的服務(wù)器上接收的 text data (文本數(shù)據(jù)),并且將 DataFrame 轉(zhuǎn)換以計(jì)算 word counts 。
// 創(chuàng)建表示從連接到 localhost:9999 的輸入行 stream 的 DataFramevallines=spark.readStream.format("socket").option("host","localhost").option("port",9999).load()// 將 lines 切分為 wordsvalwords=lines.as[String].flatMap(_.split(" "))// 生成正在運(yùn)行的 word countvalwordCounts=words.groupBy("value").count()
這個(gè)linesDataFrame 表示一個(gè)包含包含 streaming text data (流文本數(shù)據(jù)) 的無邊界表。此表包含了一列名為 “value” 的 strings ,并且 streaming text data 中的每一 line (行)都將成為表中的一 row (行)。請(qǐng)注意,這并不是正在接收的任何數(shù)據(jù),因?yàn)槲覀冎皇窃O(shè)置 transformation (轉(zhuǎn)換),還沒有開始。接下來,我們使用.as[String]將 DataFrame 轉(zhuǎn)換為 String 的 Dataset ,以便我們可以應(yīng)用flatMap操作將每 line (行)切分成多個(gè) words 。所得到的wordsDataset 包含所有的 words 。最后,我們通過將 Dataset 中 unique values (唯一的值)進(jìn)行分組并對(duì)它們進(jìn)行計(jì)數(shù)來定義wordCountsDataFrame 。請(qǐng)注意,這是一個(gè) streaming DataFrame ,它表示 stream 的正在運(yùn)行的 word counts 。
我們現(xiàn)在已經(jīng)設(shè)置了關(guān)于 streaming data (流數(shù)據(jù))的 query (查詢)。剩下的就是實(shí)際開始接收數(shù)據(jù)并計(jì)算 counts (計(jì)數(shù))。為此,我們將其設(shè)置為在每次更新時(shí)將完整地計(jì)數(shù)(由outputMode("complete")指定)發(fā)送到控制臺(tái)。然后使用start()啟動(dòng) streaming computation (流式計(jì)算)。
// 開始運(yùn)行將 running counts 打印到控制臺(tái)的查詢valquery=wordCounts.writeStream.outputMode("complete").format("console").start()query.awaitTermination()
執(zhí)行此代碼之后, streaming computation (流式計(jì)算) 將在后臺(tái)啟動(dòng)。query對(duì)象是該 active streaming query (活動(dòng)流查詢)的 handle (句柄),并且我們決定使用awaitTermination()來等待查詢的終止,以防止查詢處于 active (活動(dòng))狀態(tài)時(shí)退出。
要實(shí)際執(zhí)行此示例代碼,您可以在您自己的Spark 應(yīng)用程序編譯代碼,或者簡(jiǎn)單地運(yùn)行示例一旦您下載了 Spark 。我們正在展示的是后者。您將首先需要運(yùn)行 Netcat (大多數(shù)類 Unix 系統(tǒng)中的一個(gè)小型應(yīng)用程序)作為 data server 通過使用
$ nc -lk 9999
然后,在一個(gè)不同的終端,您可以啟動(dòng)示例通過使用
$ ./bin/run-example org.apache.spark.examples.sql.streaming.StructuredNetworkWordCount localhost9999
然后,在運(yùn)行 netcat 服務(wù)器的終端中輸入的任何 lines 將每秒計(jì)數(shù)并打印在屏幕上。它看起來像下面這樣。
# 終端 1:# 運(yùn)行 Netcat$ nc -lk9999apache sparkapache hadoop...
# 終端 2: 運(yùn)行 StructuredNetworkWordCount$ ./bin/run-example org.apache.spark.examples.sql.streaming.StructuredNetworkWordCount localhost9999-------------------------------------------Batch:0-------------------------------------------+------+-----+|value|count|+------+-----+|apache|1||spark|1|+------+-----+-------------------------------------------Batch:1-------------------------------------------+------+-----+|value|count|+------+-----+|apache|2||spark|1||hadoop|1|+------+-----+...
Structured Streaming 的關(guān)鍵思想是將 live data stream (實(shí)時(shí)數(shù)據(jù)流)視為一種正在不斷 appended (附加)的表。這形成了一個(gè)與 batch processing model (批處理模型)非常相似的新的 stream processing model (流處理模型)。您會(huì)將您的 streaming computation (流式計(jì)算)表示為在一個(gè)靜態(tài)表上的 standard batch-like query (標(biāo)準(zhǔn)類批次查詢),并且 Spark 在unbounded(無界)輸入表上運(yùn)行它作為incremental(增量)查詢。讓我們更加詳細(xì)地了解這個(gè)模型。
將 input data stream (輸入數(shù)據(jù)流) 視為 “Input Table”(輸入表)。每個(gè)在 stream 上到達(dá)的 data item (數(shù)據(jù)項(xiàng))就像是一個(gè)被 appended 到 Input Table 的新的 row 。
對(duì)輸入的查詢將生成 “Result Table” (結(jié)果表)。每個(gè) trigger interval (觸發(fā)間隔)(例如,每 1 秒),新 row (行)將附加到 Input Table ,最終更新 Result Table 。無論何時(shí)更新 result table ,我們都希望將 changed result rows (更改的結(jié)果行)寫入 external sink (外部接收器)。
“Output(輸出)” 被定義為寫入 external storage (外部存儲(chǔ)器)的內(nèi)容。可以以不同的模式定義 output :
Complete Mode(完全模式)- 整個(gè)更新的 Result Table 將被寫入外部存儲(chǔ)。由 storage connector (存儲(chǔ)連接器)決定如何處理整個(gè)表的寫入。
Append Mode(附加模式)- 只有 Result Table 中自上次觸發(fā)后附加的新 rows(行) 將被寫入 external storage (外部存儲(chǔ))。這僅適用于不期望更改 Result Table 中現(xiàn)有行的查詢。
Update Mode(更新模式)- 只有自上次觸發(fā)后 Result Table 中更新的 rows (行)將被寫入 external storage (外部存儲(chǔ))(從 Spark 2.1.1 之后可用)。請(qǐng)注意,這與 Complete Mode (完全模式),因?yàn)榇四J絻H輸出自上次觸發(fā)以來更改的 rows (行)。如果查詢不包含 aggregations (聚合),它將等同于 Append mode 。
請(qǐng)注意,每種模式適用于特定模型的查詢。這將在later詳細(xì)討論。
為了說明這個(gè)模型的使用,我們來了解一下上面章節(jié)的快速示例。第一個(gè)linesDataFrame 是 input table ,并且最后的wordCountsDataFrame 是 result table 。請(qǐng)注意,streaminglinesDataFrame 上的查詢生成wordCounts是exactly the same(完全一樣的)因?yàn)樗鼘⑹且粋€(gè) static DataFrame (靜態(tài) DataFrame )。但是,當(dāng)這個(gè)查詢啟動(dòng)時(shí), Spark 將從 socket 連接中持續(xù)檢查新數(shù)據(jù)。如果有新數(shù)據(jù),Spark 將運(yùn)行一個(gè) “incremental(增量)” 查詢,它會(huì)結(jié)合以前的 running counts (運(yùn)行計(jì)數(shù))與新數(shù)據(jù)計(jì)算更新的 counts ,如下所示。
這種模式與許多其他 stream processing engines (流處理引擎)有著顯著不同。許多 streaming systems (流系統(tǒng))要求用戶本身保持運(yùn)行 aggregations (聚合),因此必須要考慮容錯(cuò),和數(shù)據(jù)一致性(at-least-once(至少一次), at-most-once (最多一次),exactly-once (完全一次))。在這個(gè)模型中,當(dāng)有新數(shù)據(jù)時(shí), Spark 負(fù)責(zé)更新 Result Table ,從而減輕用戶對(duì)它的考慮。舉個(gè)例子,我們來看一下這個(gè)模型如何處理對(duì)于基于 event-time 的處理和 late arriving (遲到)的數(shù)據(jù)。
Event-time 是數(shù)據(jù)本身 embedded (嵌入)的時(shí)間。對(duì)于很多應(yīng)用程序,您可能需要在此 event-time 進(jìn)行操作。例如,如果要每分鐘獲取 IoT devices (設(shè)備)生成的 events 數(shù),則可能希望使用數(shù)據(jù)生成的時(shí)間(即數(shù)據(jù)中的 event-time ),而不是 Spark 接收到它們的時(shí)間。這個(gè) event-time 在這個(gè)模型中非常自然地表現(xiàn)出來 – 來自 devices (設(shè)備)的每個(gè) event 都是表中的一 row(行),并且 event-time 是 row (行)中的 column value (列值)。這允許 window-based aggregations (基于窗口的聚合)(例如每分鐘的 events 數(shù))僅僅是 event-time 列上的特殊類型的 group (分組)和 aggregation (聚合) – 每個(gè) time window 是一個(gè)組,并且每一 row (行)可以屬于多個(gè) windows/groups 。因此,可以在 static dataset (靜態(tài)數(shù)據(jù)集)(例如來自 collected device events logs (收集的設(shè)備事件日志))以及 data stream 上一致地定義 event-time-window-based aggregation queries (基于事件時(shí)間窗口的聚合查詢),從而使用戶的使用壽命更加容易。
此外,這個(gè)模型自然地處理了比預(yù)計(jì)將根據(jù)它的 event-time 到達(dá)的數(shù)據(jù)晚到的數(shù)據(jù)。由于 Spark 正在更新 Result Table , Spark 有完整的控制對(duì)當(dāng)有遲到的數(shù)據(jù)時(shí) updating old aggregates (更新舊的聚合),以及清理 old aggregates (舊聚合) 以限制 intermediate state data (中間體狀態(tài)數(shù)據(jù))的大小。自 Spark 2.1 以來,我們對(duì)于 watermarking 進(jìn)行了支持,允許用戶指定 late data 的閾值,并允許引擎相應(yīng)地清理舊狀態(tài)。這些將在后面的Window Operations部分解釋。
提供 end-to-end exactly-once semantics (端到端的完全一次性語義)是 Structured Streaming 設(shè)計(jì)背后的關(guān)鍵目標(biāo)之一。為了實(shí)現(xiàn)這一點(diǎn),我們?cè)O(shè)計(jì)了 Structured Streaming sources , sinks 和 execution engine (執(zhí)行引擎),以可靠的跟蹤處理確切進(jìn)度,以便它可以通過 restarting and/or reprocessing (重新啟動(dòng)和/或重新處理)來處理任何類型的故障。假設(shè)每個(gè) streaming source 都具有 offsets (偏移量)(類似于 Kafka offsets 或 Kinesis sequence numbers (Kafka 偏移量或 Kinesis 序列號(hào)))來跟蹤 stream 中的 read position (讀取位置)。引擎使用 checkpointing (檢查點(diǎn))并 write ahead logs (預(yù)寫日志)記錄每個(gè) trigger (觸發(fā)器)中正在處理的數(shù)據(jù)的 offset range (偏移范圍)。 streaming sinks 設(shè)計(jì)為處理后處理的 idempotent (冪等)。一起使用 replayable sources (可重放源)和 idempotent sinks (冪等接收器), Structured Streaming 可以確保在任何故障下end-to-end exactly-once semantics(端對(duì)端完全一次性語義)。
自從 Spark 2.0 , DataFrame 和 Datasets 可以表示 static (靜態(tài)), bounded data(有界數(shù)據(jù)),以及 streaming , unbounded data (無界數(shù)據(jù))。類似于 static Datasets/DataFrames ,您可以使用常用的 entry point (入口點(diǎn))SparkSession(Scala/Java/Python/R文檔) 來從 streaming sources 中創(chuàng)建 streaming DataFrames/Datasets ,并將其作為 static DataFrames/Datasets 應(yīng)用相同的操作。如果您不熟悉 Datasets/DataFrames ,強(qiáng)烈建議您使用DataFrame/Dataset 編程指南來熟悉它們。
創(chuàng)建 streaming DataFrames 和 streaming Datasets
可以通過DataStreamReader的接口 (Scala/Java/Python文檔 )來創(chuàng)建 Streaming DataFrames 并由SparkSession.readStream()返回。在R中,使用read.stream()方法。與創(chuàng)建 static DataFrame 的 read interface (讀取接口)類似,您可以指定 source - data format (數(shù)據(jù)格式), schema (模式), options (選項(xiàng))等的詳細(xì)信息。
在 Spark 2.0 中,有一些內(nèi)置的 sources 。
File source(文件源)- 以文件流的形式讀取目錄中寫入的文件。支持的文件格式為 text , csv , json , parquet 。有關(guān)更多的 up-to-date 列表,以及每種文件格式的支持選項(xiàng),請(qǐng)參閱 DataStreamReader interface 的文檔。請(qǐng)注意,文件必須以 atomically (原子方式)放置在給定的目錄中,這在大多數(shù)文件系統(tǒng)中可以通過文件移動(dòng)操作實(shí)現(xiàn)。
Kafka source(Kafka 源)- 來自 Kafka 的 Poll 數(shù)據(jù)。它與 Kafka broker 的 0.10.0 或者更高的版本兼容。有關(guān)詳細(xì)信息,請(qǐng)參閱Kafka Integration 指南。
Socket source (for testing) (Socket 源(用于測(cè)試))- 從一個(gè) socket 連接中讀取 UTF8 文本數(shù)據(jù)。 listening server socket (監(jiān)聽服務(wù)器 socket)位于 driver 。請(qǐng)注意,這只能用于測(cè)試,因?yàn)樗惶峁?end-to-end fault-tolerance (端到端的容錯(cuò))保證。
某些 sources 是不容錯(cuò)的,因?yàn)樗鼈儾荒鼙WC數(shù)據(jù)在使用 checkpointed offsets (檢查點(diǎn)偏移量)故障之后可以被重新使用。參見前面的部分fault-tolerance semantics。以下是 Spark 中所有 sources 的詳細(xì)信息。
SourceOptions(選項(xiàng))Fault-tolerant(容錯(cuò))Notes(說明)
File source(文件源)path: 輸入路徑的目錄,并且與所有文件格式通用。
maxFilesPerTrigger: 每個(gè) trigger (觸發(fā)器)中要考慮的最大新文件數(shù)(默認(rèn)是: 無最大值)
latestFirst: 是否先處理最新的新文件,當(dāng)有大量積壓的文件時(shí)有用(默認(rèn): false)
fileNameOnly: 是否僅根據(jù)文件名而不是完整路徑檢查新文件(默認(rèn)值: false)。將此設(shè)置為 `true` ,以下文件將被視為相同的文件,因?yàn)樗鼈兊奈募?"dataset.txt" 是相同的:
· "file:///dataset.txt"
· "s3://a/dataset.txt"
· "s3n://a/b/dataset.txt"
· "s3a://a/b/c/dataset.txt"
有關(guān)特定于 file-format-specific (文件格式)的選項(xiàng),請(qǐng)參閱DataStreamReader(Scala/Java/Python/R) 中的相關(guān)方法。例如,對(duì)于 "parquet" 格式選項(xiàng)請(qǐng)參閱DataStreamReader.parquet()Yes支持 glob 路徑,但是不支持多個(gè)逗號(hào)分隔的 paths/globs 。
Socket Source(Socket 源)host: 連接到的 host ,必須指定
port: 連接的 port (端口),必須指定No
Kafka Source(Kafka 源)請(qǐng)查看Kafka Integration 指南.Yes
這里有一些例子。
valspark:SparkSession=...// 從 socket 讀取 textvalsocketDF=spark.readStream.format("socket").option("host","localhost").option("port",9999).load()socketDF.isStreaming// 對(duì)于有 streaming sources 的 DataFrame 返回 TruesocketDF.printSchema// 讀取目錄內(nèi)原子寫入的所有 csv 文件valuserSchema=newStructType().add("name","string").add("age","integer")valcsvDF=spark.readStream.option("sep",";").schema(userSchema)// 指定 csv 文件的模式.csv("/path/to/directory")// 等同于 format("csv").load("/path/to/directory")
這些示例生成無類型的 streaming DataFrames ,這意味著在編譯時(shí)不會(huì)檢查 DataFrame 的模式,僅在運(yùn)行時(shí)在 query is submitted (查詢提交)的時(shí)候進(jìn)行檢查。像map,flatMap等這樣的操作需要在編譯時(shí)知道這個(gè)類型。要做到這一點(diǎn),您可以使用與 static DataFrame 相同的方法將這些 untyped (無類型)的 streaming DataFrames 轉(zhuǎn)換為 typed streaming Datasets (類型化的 streaming Datasets )。有關(guān)詳細(xì)信息,請(qǐng)參閱SQL 編程指南。此外,有關(guān)支持的 streaming sources 的更多詳細(xì)信息將在文檔后面討論。
streaming DataFrames/Datasets 的模式接口和分區(qū)
默認(rèn)情況下,基于文件的 sources 的 Structured Streaming 需要您指定 schema (模式),而不是依靠 Spark 自動(dòng) infer 。這種 restriction 確保了 consistent schema (一致的模式)將被用于 streaming query (流式查詢),即使在出現(xiàn)故障的情況下也是如此。對(duì)于 ad-hoc use cases (特殊用例),您可以通過將spark.sql.streaming.schemaInference設(shè)置為true來重新啟用 schema inference (模式接口)。
當(dāng)存在名為/key=value/的子目錄并且列表將自動(dòng)遞歸到這些目錄中時(shí),會(huì)發(fā)生 Partition discovery (分區(qū)發(fā)現(xiàn))。如果這些 columns (列)顯示在用戶提供的 schema 中,則它們將根據(jù)正在讀取的文件路徑由 Spark 進(jìn)行填充。 構(gòu)成 partitioning scheme (分區(qū)方案)的目錄 must be present when the query starts (必須在查詢開始時(shí)是存在的),并且必須保持 static 。例如,當(dāng)/data/year=2015/存在時(shí),可以添加/data/year=2016/,但是更改 partitioning column (分區(qū)列)是無效的(即通過創(chuàng)建目錄/data/date=2016-04-17/)。
streaming DataFrames/Datasets 上的操作
您可以對(duì) streaming DataFrames/Datasets 應(yīng)用各種操作 - 從 untyped (無類型), SQL-like operations (類似 SQL 的操作)(例如select,where,groupBy) 到 typed RDD-like operations (類型化的類似 RDD 的操作)(例如map,filter,flatMap)。有關(guān)詳細(xì)信息,請(qǐng)參閱SQL 編程指南。讓我們來看看可以使用的幾個(gè)示例操作。
基礎(chǔ)操作 - Selection, Projection, Aggregation
streaming 支持 DataFrame/Dataset 上的大多數(shù)常見操作。不支持的少數(shù)操作discussed later將在本節(jié)中討論(稍后討論)。
caseclassDeviceData(device:String,deviceType:String,signal:Double,time:DateTime)valdf:DataFrame=...// streaming DataFrame with IOT device data with schema { device: string, deviceType: string, signal: double, time: string }valds:Dataset[DeviceData]=df.as[DeviceData]// streaming Dataset with IOT device data// Select the devices which have signal more than 10df.select("device").where("signal > 10")// using untyped APIsds.filter(_.signal>10).map(_.device)// using typed APIs// Running count of the number of updates for each device typedf.groupBy("deviceType").count()// using untyped API// Running average signal for each device typeimportorg.apache.spark.sql.expressions.scalalang.typedds.groupByKey(_.deviceType).agg(typed.avg(_.signal))// using typed API
Window Operations on Event Time (事件時(shí)間窗口操作)
通過 Structured Streaming , sliding event-time window (滑動(dòng)事件時(shí)間窗口)的 Aggregations (聚合)很簡(jiǎn)單,與 grouped aggregations (分組聚合)非常相似。在 grouped aggregation (分組聚合)中,為 user-specified grouping column (用戶指定的分組列)中的每個(gè)唯一值維護(hù) aggregate values (聚合值)(例如 counts )。在 window-based aggregations (基于窗口的聚合)的情況下,針對(duì)每個(gè)窗口的 event-time 維持 aggregate values (聚合值)。讓我們用一個(gè)例子來理解這一點(diǎn)。
想象一下,我們的快速示例被修改,并且 stream 現(xiàn)在包含生成 line 的時(shí)間的 line 。不運(yùn)行 word counts ,我們想 count words within 10 minute windows (在 10 分鐘內(nèi)的窗口計(jì)數(shù)單詞),每 5 分鐘更新一次。也就是說,在 10 minute windows (10 分鐘的窗口之間)收到的 word counts 12:00 - 12:10, 12:05 - 12:15, 12:10 - 12:20 等。請(qǐng)注意, 12:00 - 12:10 表示數(shù)據(jù)在 12:00 之后但在 12:10 之前抵達(dá)。現(xiàn)在,考慮在 12:07 收到一個(gè) word 。這個(gè) word 應(yīng)該增加對(duì)應(yīng)于兩個(gè)窗口的計(jì)數(shù) 12:00 - 12:10 和 12:05 - 12:15 。因此, counts 將被二者分組, grouping key (分組秘鑰)(即 word)和 window (窗口)(可以從 event-time 計(jì)算)來 indexed (索引)。
result tables 將如下所示。
由于這個(gè) windowing (窗口)類似于 grouping (分組),在代碼中,您可以使用groupBy()和window()操作來表示 windowed aggregations (窗口化的聚合)。您可以看到以下示例Scala/Java/Python的完整代碼。
importspark.implicits._valwords=...// streaming DataFrame of schema { timestamp: Timestamp, word: String }// Group the data by window and word and compute the count of each groupvalwindowedCounts=words.groupBy(window($"timestamp","10 minutes","5 minutes"),$"word").count()
處理 Late Data (遲到數(shù)據(jù))和 Watermarking (水印)
現(xiàn)在考慮以下如果其中一個(gè) event 遲到應(yīng)用程序會(huì)發(fā)生什么。例如,想象一下,在 12:04 (即 event time )生成的 word 可以在 12:11 被接收申請(qǐng)。應(yīng)用程序應(yīng)該使用 12:04 而不是 12:11 來更新 window12:00 - 12:10的較舊 counts 。發(fā)生這種情況自然就是在我們 window-based grouping (基于窗口的分組中) - Structured Streaming 可以保持intermediate state 對(duì)于部分 aggregates (聚合)長(zhǎng)時(shí)間,以便后期數(shù)據(jù)可以 update aggregates of old windows correctly (更新聚合)舊窗口正確,如下圖所示。
但是,要運(yùn)行此查詢幾天,系統(tǒng)必須綁定 the amount of intermediate in-memory state it accumulates (中間狀態(tài)累積的數(shù)量)。這意味著系統(tǒng)需要知道什么時(shí)候 old aggregate (老聚合)可以從內(nèi)存中的狀態(tài)丟失,因?yàn)檫@個(gè)應(yīng)用程序不會(huì)在繼續(xù)接收 aggregate (該聚合)的更多l(xiāng)ate data (后期的數(shù)據(jù))。為了實(shí)現(xiàn)這一點(diǎn),在 Spark 2.1 中,我們介紹了watermarking(水印),讓引擎自動(dòng)跟蹤數(shù)據(jù)中的 current event time (當(dāng)前事件時(shí)間)并試圖相應(yīng)地清理舊狀態(tài)。您可以定義查詢的 watermark 指定 event time column (事件時(shí)間列)和數(shù)據(jù)預(yù)期的延遲閾值 event time (事件時(shí)間)。對(duì)于從T時(shí)間開始的特定窗口,引擎將保持狀態(tài)并允許 late data (延遲數(shù)據(jù))更新狀態(tài)直到(max event time seen by the engine - late threshold > T)。換句話說, threshold (閾值)內(nèi)的 late data (晚期數(shù)據(jù))將被 aggregated ,但數(shù)據(jù)晚于閾值將被丟棄。讓我們以一個(gè)例子來理解這一點(diǎn)。我們可以使用withWatermark()可以輕松地定義上一個(gè)例子的 watermarking (水印),如下所示。
importspark.implicits._valwords=...// streaming DataFrame of schema { timestamp: Timestamp, word: String }// Group the data by window and word and compute the count of each groupvalwindowedCounts=words.withWatermark("timestamp","10 minutes").groupBy(window($"timestamp","10 minutes","5 minutes"),$"word").count()
在這個(gè)例子中,我們正在定義查詢的 watermark 對(duì) “timestamp” 列的值,并將 “10 minutes” 定義為允許數(shù)據(jù)延遲的閾值。如果這個(gè)查詢以 Update output mode (更新輸出模式)運(yùn)行(稍后在Output Modes部分中討論),引擎將不斷更新 Result Table 中窗口的 counts ,直到 window is older than the watermark (窗口比水印較舊),它滯后于 current event time (當(dāng)前事件時(shí)間)列 “timestamp” 10分鐘。這是一個(gè)例子。
如圖所示,maximum event time tracked (引擎跟蹤的最大事件時(shí)間)是藍(lán)色虛線,watermark 設(shè)置為(max event time - '10 mins')在每個(gè)觸發(fā)的開始處是紅線。例如,當(dāng)引擎觀察數(shù)據(jù)(12:14, dog)時(shí),它為下一個(gè)觸發(fā)器設(shè)置 watermark 為12:04。該 watermark 允許 engine 保持 intermediate state (中間狀態(tài))另外 10 分鐘以允許延遲 late data to be counted (要計(jì)數(shù)的數(shù)據(jù))。例如,數(shù)據(jù)(12:09, cat)是 out of order and late (不正常的,而且延遲了),它落在了 windows12:05 - 12:15和12:10 - 12:20。因?yàn)樗匀辉?watermark12:04之前的觸發(fā)器,引擎仍然將 intermediate counts (中間計(jì)數(shù))保持為狀態(tài)并正確 updates the counts of the related windows (更新相關(guān)窗口的計(jì)數(shù))。然而,當(dāng) watermark 更新為12:11時(shí),window(12:00 - 12:10)的中間狀態(tài)被清除,所有 subsequent data (后續(xù)數(shù)據(jù))(例如(12:04, donkey))被認(rèn)為是 “too late” ,因此被忽視。請(qǐng)注意,每次觸發(fā)后,寫入 updated counts (更新的計(jì)數(shù))(即紫色行)作為 trigger output 進(jìn)行 sink ,如下 Update mode 所示。
某些 sinks (接收器)(例如 文件)可能不支持更新模式所需的 fine-grained updates (細(xì)粒度更新)。 與他們一起工作,我們也支持 Append Mode (附加模式),只有final counts(最終計(jì)數(shù))被寫入 sink 。這如下所示。
請(qǐng)注意,在 non-streaming Dataset (非流數(shù)據(jù)集)上使用withWatermark是不可行的。 由于 watermark 不應(yīng)該以任何方式影響任何批處理查詢,我們將直接忽略它。
與之前的 Update Mode 類似,引擎維護(hù) intermediate counts for each window (每個(gè)窗口的中間計(jì)數(shù))。但是,partial counts (部分計(jì)數(shù))不會(huì)更新到 Result Table ,也不是寫入 sink 。 引擎等待遲到的 “10 mins” 計(jì)數(shù),然后刪除 window < watermark 的 intermediate state (中間狀態(tài)),并追加最終 計(jì)數(shù)到 Result Table/sink 。 例如, window12:00 - 12:10的最終計(jì)數(shù)是僅在水印更新為12:11之后附加到 Result Table 。
Conditions for watermarking to clean aggregation state(watermarking 清理聚合狀態(tài)的條件)重要的是要注意,watermarking 必須滿足以下清理聚合查詢中的狀態(tài)的條件(從 Spark 2.1.1 開始,將來會(huì)更改)。
Output mode must be Append or Update.(輸出模式必須是追加或者更新)Complete mode 要求保留所有 aggregate data (聚合數(shù)據(jù)),因此不能使用 watermarking 去掉 intermediate state (中間狀態(tài))。參見Output Modes部分,詳細(xì)說明每種輸出模式的語義。
aggregation (聚合)必須具有 event-time column (事件時(shí)間列)或 event-time column 上的window。
withWatermark必須被調(diào)用與聚合中使用的 timestamp column (時(shí)間戳列)相同的列。例如,df.withWatermark("time", "1 min").groupBy("time2").count()在 Append output mode 是無效的,因?yàn)?watermark 是從聚合列在不同的列上定義的。
在使用 watermark details 的 aggregation (聚合)之前必須調(diào)用withWatermark。例如,df.groupBy("time").count().withWatermark("time", "1 min")在 Append output mode 中是無效的。
Streaming DataFrames 可以與 static DataFrames 連接,以創(chuàng)建新的 streaming DataFrames 。 這里有幾個(gè)例子。
valstaticDf=spark.read....valstreamingDf=spark.readStream....streamingDf.join(staticDf,"type")// inner equi-join with a static DFstreamingDf.join(staticDf,"type","right_join")// right outer join with a static DF
Streaming Deduplication (Streaming 去重)
您可以使用 events 中的 unique identifier (唯一標(biāo)識(shí)符)對(duì) data streams 中的記錄進(jìn)行重復(fù)數(shù)據(jù)刪除。 這與使用唯一標(biāo)識(shí)符列的 static 重復(fù)數(shù)據(jù)消除完全相同。 該查詢將存儲(chǔ)先前記錄所需的數(shù)據(jù)量,以便可以過濾重復(fù)的記錄。 與 aggregations (聚合)類似,您可以使用帶有或不帶有 watermarking 的重復(fù)數(shù)據(jù)刪除功能。
With watermark(使用 watermark )- 如果重復(fù)記錄可能到達(dá)的時(shí)間有上限,則可以在 event time column (事件時(shí)間列)上定義 watermark ,并使用 guid 和 event time columns 進(jìn)行重復(fù)數(shù)據(jù)刪除。 該查詢將使用 watermark 從以前的記錄中刪除舊的狀態(tài)數(shù)據(jù),這些記錄不會(huì)再受到任何重復(fù)。 這界定了查詢必須維護(hù)的狀態(tài)量。
Without watermark (不適用 watermark )- 由于當(dāng)重復(fù)記錄可能到達(dá)時(shí)沒有界限,查詢將來自所有過去記錄的數(shù)據(jù)存儲(chǔ)為狀態(tài)。
valstreamingDf=spark.readStream....// columns: guid, eventTime, ...// Without watermark using guid columnstreamingDf.dropDuplicates("guid")// With watermark using guid and eventTime columnsstreamingDf.withWatermark("eventTime","10 seconds").dropDuplicates("guid","eventTime")
Arbitrary Stateful Operations (任意有狀態(tài)的操作)
許多用例需要比 aggregations 更高級(jí)的狀態(tài)操作。例如,在許多用例中,您必須 track (跟蹤) data streams of events (事件數(shù)據(jù)流)中的 sessions (會(huì)話)。對(duì)于進(jìn)行此類 sessionization (會(huì)話),您必須將 arbitrary types of data (任意類型的數(shù)據(jù))保存為 state (狀態(tài)),并在每個(gè) trigger 中使用 state using the data stream events (數(shù)據(jù)流事件對(duì)狀態(tài))執(zhí)行 arbitrary operations 。自從 Spark 2.2 ,可以使用mapGroupsWithState操作和更強(qiáng)大的操作flatMapGroupsWithState來完成。這兩個(gè)操作都允許您在 grouped Datasets (分組的數(shù)據(jù)集)上應(yīng)用用戶定義的代碼來更新用戶定義的狀態(tài)。有關(guān)更具體的細(xì)節(jié),請(qǐng)查看 API文檔(Scala/Java) 和例子 (Scala/Java)。
streaming DataFrames/Datasets 不支持一些 DataFrame/Dataset 操作。其中一些如下。
streaming Datasets 不支持 Multiple streaming aggregations (多個(gè)流聚合) (i.e. a chain of aggregations on a streaming DF)(即 streaming DF 上的聚合鏈)
streaming Datasets 不支持 Limit and take first N rows 。
streaming Datasets 上的 Distinct operations 不支持。
只有在 aggregation 和 Complete Output Mode 下,streaming Datasets 才支持排序操作。
有條件地支持 streaming 和 static Datasets 之間的 Outer joins 。
不支持使用 streaming Dataset 的 Full outer join
不支持在右側(cè)使用 streaming Dataset 的 Left outer join
不支持在左側(cè)使用 streaming Dataset 的 Right outer join
不支持兩種 streaming Datasets 之間的任何種類的 joins 。
此外,還有一些 Dataset 方法將不適用于 streaming Datasets 。他們是立即運(yùn)行查詢并返回結(jié)果的操作,這在 streaming Dataset 上沒有意義。相反,這些功能可以通過顯式啟動(dòng) streaming query 來完成(參見下一節(jié))。
count()- 無法從 streaming Dataset 返回 single count 。 而是使用ds.groupBy().count()返回一個(gè)包含 running count 的 streaming Dataset 。
foreach()- 而是使用ds.writeStream.foreach(...)(參見下一節(jié)).
show()- 而是使用 console sink (參見下一節(jié)).
如果您嘗試任何這些操作,您將看到一個(gè)AnalysisException,如 “operation XYZ is not supported with streaming DataFrames/Datasets” 。雖然其中一些可能在未來版本的 Spark 中得到支持,還有其他一些從根本上難以有效地實(shí)現(xiàn) streaming data 。例如, input stream 的排序不受支持,因?yàn)樗枰A?track of all the data received in the stream (跟蹤流中接收到的所有數(shù)據(jù))。 因此從根本上難以有效率地執(zhí)行。
一旦定義了 final result DataFrame/Dataset ,剩下的就是讓你開始 streaming computation 。 為此,您必須使用DataStreamWriter(Scala/Java/Python文檔)通過Dataset.writeStream()返回。您將必須在此 interface 中指定以下一個(gè)或多個(gè)。
Details of the output sink ( output sink 的詳細(xì)信息):Data format, location, etc.
Output mode (輸出模式):指定寫入 output sink 的內(nèi)容。
Query name (查詢名稱):可選,指定用于標(biāo)識(shí)的查詢的唯一名稱。
Trigger interval (觸發(fā)間隔):可選,指定觸發(fā)間隔。 如果未指定,則系統(tǒng)將在上一次處理完成后立即檢查新數(shù)據(jù)的可用性。 如果由于先前的處理尚未完成而導(dǎo)致觸發(fā)時(shí)間錯(cuò)誤,則系統(tǒng)將嘗試在下一個(gè)觸發(fā)點(diǎn)觸發(fā),而不是在處理完成后立即觸發(fā)。
Checkpoint location (檢查點(diǎn)位置):對(duì)于可以保證 end-to-end fault-tolerance (端對(duì)端容錯(cuò))能力的某些 output sinks ,請(qǐng)指定系統(tǒng)將寫入所有 checkpoint (檢查點(diǎn))信息的位置。 這應(yīng)該是與 HDFS 兼容的容錯(cuò)文件系統(tǒng)中的目錄。 檢查點(diǎn)的語義將在下一節(jié)中進(jìn)行更詳細(xì)的討論。
有幾種類型的輸出模式。
Append mode (default) (附加模式(默認(rèn)))- 這是默認(rèn)模式,其中只有 自從 last trigger (上一次觸發(fā))以來,添加到 Result Table 的新行將會(huì)是 outputted to the sink 。 只有添加到 Result Table 的行將永遠(yuǎn)不會(huì)改變那些查詢才支持這一點(diǎn)。 因此,這種模式 保證每行只能輸出一次(假設(shè) fault-tolerant sink )。例如,只有select,where,map,flatMap,filter,join等查詢支持 Append mode 。
Complete mode (完全模式)- 每次觸發(fā)后,整個(gè) Result Table 將被輸出到 sink 。 aggregation queries (聚合查詢)支持這一點(diǎn)。
Update mode (更新模式)- (自 Spark 2.1.1 可用) 只有 Result Table rows 自上次觸發(fā)后更新將被輸出到 sink 。更多信息將在以后的版本中添加。
不同類型的 streaming queries 支持不同的 output modes 。 以下是兼容性矩陣。
Query Type(查詢類型)
Supported Output Modes(支持的輸出模式)Notes(說明)
Queries with aggregation (使用聚合的查詢)Aggregation on event-time with watermark (使用 watermark 的 event-time 聚合 )Append, Update, Complete (附加,更新,完全)Append mode 使用 watermark 來降低 old aggregation state (舊聚合狀態(tài))。 但輸出 windowed aggregation (窗口聚合)延遲在 `withWatermark()` 中指定的 late threshold (晚期閾值)模式語義,rows 只能在 Result Table 中添加一次在 finalized (最終確定)之后(即 watermark is crossed (水印交叉)后)。 有關(guān)詳細(xì)信息,請(qǐng)參閱Late Data部分。
Update mode 使用 watermark 刪除 old aggregation state (舊的聚合狀態(tài))。
Complete mode (完全模式)不會(huì)刪除舊的聚合狀態(tài),因?yàn)閺亩x這個(gè)模式 ???????? 保留 Result Table 中的所有數(shù)據(jù)。
Other aggregations (其他聚合)Complete, Update (完全,更新)由于沒有定義 watermark(僅在其他 category 中定義),舊的聚合狀態(tài)不會(huì)刪除。
不支持 Append mode ,因?yàn)?aggregates (聚合)可以更新,從而違反了這種模式的語義。
Queries withmapGroupsWithStateUpdate (更新)
Queries withflatMapGroupsWithStateAppend operation mode (附加操作模式)Append (附加)flatMapGroupsWithState之后允許 Aggregations (聚合)。
Update operation mode (更新操作模式)Update(更新)flatMapGroupsWithState之后不允許 Aggregations (聚合)。
Other queries (其他查詢)Append, Update (附加,更新)不支持 Complete mode ,因?yàn)閷⑺形捶纸M數(shù)據(jù)保存在 Result Table 中是不可行的 。
有幾種類型的內(nèi)置輸出接收器。
File sink (文件接收器)- 將輸出存儲(chǔ)到目錄中。
writeStream.format("parquet")// can be "orc", "json", "csv", etc..option("path","path/to/destination/dir").start()
Foreach sink- 對(duì) output 中的記錄運(yùn)行 arbitrary computation 。 有關(guān)詳細(xì)信息,請(qǐng)參閱本節(jié)后面部分。
writeStream.foreach(...).start()
Console sink (for debugging) (控制臺(tái)接收器(用于調(diào)試))- 每次觸發(fā)時(shí),將輸出打印到 console/stdout 。 都支持 Append 和 Complete 輸出模式。 這應(yīng)該用于低數(shù)據(jù)量的調(diào)試目的,因?yàn)樵诿看斡|發(fā)后,整個(gè)輸出被收集并存儲(chǔ)在驅(qū)動(dòng)程序的內(nèi)存中。
writeStream.format("console").start()
Memory sink (for debugging) (內(nèi)存 sink (用于調(diào)試))- 輸出作為 in-memory table (內(nèi)存表)存儲(chǔ)在內(nèi)存中。都支持 Append 和 Complete 輸出模式。 這應(yīng)該用于調(diào)試目的在低數(shù)據(jù)量下,整個(gè)輸出被收集并存儲(chǔ)在驅(qū)動(dòng)程序的存儲(chǔ)器中。因此,請(qǐng)謹(jǐn)慎使用。
writeStream.format("memory").queryName("tableName").start()
某些 sinks 是不容錯(cuò)的,因?yàn)樗鼈儾荒鼙WC輸出的持久性并且僅用于調(diào)試目的。參見前面的部分容錯(cuò)語義。以下是 Spark 中所有接收器的詳細(xì)信息。
Sink (接收器)Supported Output Modes (支持的輸出模式)Options (選項(xiàng))Fault-tolerant (容錯(cuò))Notes (說明)
File Sink (文件接收器)Append (附加)path: 必須指定輸出目錄的路徑。
有關(guān)特定于文件格式的選項(xiàng),請(qǐng)參閱 DataFrameWriter (Scala/Java/Python/R) 中的相關(guān)方法。 例如,對(duì)于 "parquet" 格式選項(xiàng),請(qǐng)參閱DataFrameWriter.parquet()Yes支持對(duì) partitioned tables (分區(qū)表)的寫入。按時(shí)間 Partitioning (劃分)可能是有用的。
Foreach SinkAppend, Update, Compelete (附加,更新,完全)None取決于 ForeachWriter 的實(shí)現(xiàn)。更多詳細(xì)信息在下一節(jié)
Console Sink (控制臺(tái)接收器)Append, Update, Complete (附加,更新,完全)numRows: 每個(gè)觸發(fā)器需要打印的行數(shù)(默認(rèn):20)
truncate: 如果輸出太長(zhǎng)是否截?cái)啵J(rèn): true)No
Memory Sink (內(nèi)存接收器)Append, Complete (附加,完全)None否。但是在 Complete Mode 模式下,重新啟動(dòng)的查詢將重新創(chuàng)建完整的表。Table name is the query name.(表名是查詢的名稱)
請(qǐng)注意,您必須調(diào)用start()來實(shí)際啟動(dòng)查詢的執(zhí)行。 這將返回一個(gè) StreamingQuery 對(duì)象,它是連續(xù)運(yùn)行的執(zhí)行的句柄。 您可以使用此對(duì)象來管理查詢,我們將在下一小節(jié)中討論。 現(xiàn)在,讓我們通過幾個(gè)例子了解所有這些。
// ========== DF with no aggregations ==========valnoAggDF=deviceDataDf.select("device").where("signal > 10")// Print new data to consolenoAggDF.writeStream.format("console").start()// Write new data to Parquet filesnoAggDF.writeStream.format("parquet").option("checkpointLocation","path/to/checkpoint/dir").option("path","path/to/destination/dir").start()// ========== DF with aggregation ==========valaggDF=df.groupBy("device").count()// Print updated aggregations to consoleaggDF.writeStream.outputMode("complete").format("console").start()// Have all the aggregates in an in-memory tableaggDF.writeStream.queryName("aggregates")// this query name will be the table name.outputMode("complete").format("memory").start()spark.sql("select * from aggregates").show()// interactively query in-memory table
foreach操作允許在輸出數(shù)據(jù)上計(jì)算 arbitrary operations 。從 Spark 2.1 開始,這只適用于 Scala 和 Java 。為了使用這個(gè),你必須實(shí)現(xiàn)接口ForeachWriter(Scala/Java文檔) 其具有在 trigger (觸發(fā)器)之后生成 sequence of rows generated as output (作為輸出的行的序列)時(shí)被調(diào)用的方法。請(qǐng)注意以下要點(diǎn)。
writer 必須是 serializable (可序列化)的,因?yàn)樗鼘⒈恍蛄谢l(fā)送給 executors 執(zhí)行。
所有這三個(gè)方法,open,process和close都會(huì)在執(zhí)行器上被調(diào)用。
只有當(dāng)調(diào)用open方法時(shí),writer 才能執(zhí)行所有的初始化(例如打開連接,啟動(dòng)事務(wù)等)。請(qǐng)注意,如果在創(chuàng)建對(duì)象時(shí)立即在類中進(jìn)行任何初始化,那么該初始化將在 driver 中發(fā)生(因?yàn)檫@是正在創(chuàng)建的實(shí)例),這可能不是您打算的。
version和partition是open中的兩個(gè)參數(shù),它們獨(dú)特地表示一組需要被 pushed out 的行。version是每個(gè)觸發(fā)器增加的單調(diào)遞增的 id 。partition是一個(gè)表示輸出分區(qū)的 id ,因?yàn)檩敵鍪欠植际降模瑢⒃诙鄠€(gè)執(zhí)行器上處理。
open可以使用version和partition來選擇是否需要寫入行的順序。因此,它可以返回true(繼續(xù)寫入)或false( 不需要寫入 )。如果返回false,那么process不會(huì)在任何行上被調(diào)用。例如,在 partial failure (部分失敗)之后,失敗的觸發(fā)器的一些輸出分區(qū)可能已經(jīng)被提交到數(shù)據(jù)庫(kù)。基于存儲(chǔ)在數(shù)據(jù)庫(kù)中的 metadata (元數(shù)據(jù)), writer 可以識(shí)別已經(jīng)提交的分區(qū),因此返回 false 以跳過再次提交它們。
當(dāng)open被調(diào)用時(shí),close也將被調(diào)用(除非 JVM 由于某些錯(cuò)誤而退出)。即使open返回 false 也是如此。如果在處理和寫入數(shù)據(jù)時(shí)出現(xiàn)任何錯(cuò)誤,那么close將被錯(cuò)誤地調(diào)用。您有責(zé)任清理以open創(chuàng)建的狀態(tài)(例如,連接,事務(wù)等),以免資源泄漏。
在啟動(dòng)查詢時(shí)創(chuàng)建的StreamingQuery對(duì)象可用于 monitor and manage the query (監(jiān)視和管理查詢)。
valquery=df.writeStream.format("console").start()// get the query objectquery.id// get the unique identifier of the running query that persists across restarts from checkpoint dataquery.runId// get the unique id of this run of the query, which will be generated at every start/restartquery.name// get the name of the auto-generated or user-specified namequery.explain()// print detailed explanations of the queryquery.stop()// stop the queryquery.awaitTermination()// block until query is terminated, with stop() or with errorquery.exception// the exception if the query has been terminated with errorquery.recentProgress// an array of the most recent progress updates for this queryquery.lastProgress// the most recent progress update of this streaming query
您可以在單個(gè) SparkSession 中啟動(dòng)任意數(shù)量的查詢。 他們都將同時(shí)運(yùn)行共享集群資源。 您可以使用sparkSession.streams()獲取StreamingQueryManager(Scala/Java/Python文檔) 可用于管理 currently active queries (當(dāng)前活動(dòng)的查詢)。
valspark:SparkSession=...spark.streams.active// get the list of currently active streaming queriesspark.streams.get(id)// get a query object by its unique idspark.streams.awaitAnyTermination()// block until any one of them terminates
有兩個(gè)用于 monitoring and debugging active queries (監(jiān)視和調(diào)試活動(dòng)查詢) 的 API - interactively 和 asynchronously 。
您可以直接獲取活動(dòng)查詢的當(dāng)前狀態(tài)和指標(biāo)使用streamingQuery.lastProgress()和streamingQuery.status()。lastProgress()返回一個(gè)StreamingQueryProgress對(duì)象 在Scala和Java和 Python 中具有相同字段的字典。它有所有的信息在 stream 的最后一個(gè)觸發(fā)器中取得的 progress - 處理了哪些數(shù)據(jù),處理率是多少,延遲等等。streamingQuery.recentProgress返回最后幾個(gè)進(jìn)度的 array 。
另外,streamingQuery.status()返回一個(gè)StreamingQueryStatus對(duì)象在Scala和Java和 Python 中具有相同字段的字典。它提供有關(guān)的信息立即執(zhí)行的查詢 - 觸發(fā)器是否 active ,數(shù)據(jù)是否正在處理等。
這里有幾個(gè)例子。
valquery:StreamingQuery=...println(query.lastProgress)/* Will print something like the following.{"id" : "ce011fdc-8762-4dcb-84eb-a77333e28109","runId" : "88e2ff94-ede0-45a8-b687-6316fbef529a","name" : "MyQuery","timestamp" : "2016-12-14T18:45:24.873Z","numInputRows" : 10,"inputRowsPerSecond" : 120.0,"processedRowsPerSecond" : 200.0,"durationMs" : {"triggerExecution" : 3,"getOffset" : 2},"eventTime" : {"watermark" : "2016-12-14T18:45:24.873Z"},"stateOperators" : [ ],"sources" : [ {"description" : "KafkaSource[Subscribe[topic-0]]","startOffset" : {"topic-0" : {"2" : 0,"4" : 1,"1" : 1,"3" : 1,"0" : 1}},"endOffset" : {"topic-0" : {"2" : 0,"4" : 115,"1" : 134,"3" : 21,"0" : 534}},"numInputRows" : 10,"inputRowsPerSecond" : 120.0,"processedRowsPerSecond" : 200.0} ],"sink" : {"description" : "MemorySink"}}*/println(query.status)/*? Will print something like the following.{"message" : "Waiting for data to arrive","isDataAvailable" : false,"isTriggerActive" : false}*/
您還可以 asynchronously monitor (異步監(jiān)視)與SparkSession相關(guān)聯(lián)的所有查詢 通過附加一個(gè)StreamingQueryListener(Scala/Javadocs) 。一旦你使用sparkSession.streams.attachListener()附加你的自定義StreamingQueryListener對(duì)象,當(dāng)您啟動(dòng)查詢和當(dāng)有活動(dòng)查詢有進(jìn)度時(shí)停止時(shí),您將收到 callbacks (回調(diào))。 這是一個(gè)例子,
valspark:SparkSession=...spark.streams.addListener(newStreamingQueryListener(){overridedefonQueryStarted(queryStarted:QueryStartedEvent):Unit={println("Query started: "+queryStarted.id)}overridedefonQueryTerminated(queryTerminated:QueryTerminatedEvent):Unit={println("Query terminated: "+queryTerminated.id)}overridedefonQueryProgress(queryProgress:QueryProgressEvent):Unit={println("Query made progress: "+queryProgress.progress)}})
Recovering from Failures with Checkpointing (從檢查點(diǎn)恢復(fù)故障)
如果發(fā)生 failure or intentional shutdown (故障或故意關(guān)機(jī)),您可以恢復(fù)之前的查詢的進(jìn)度和狀態(tài),并繼續(xù)停止的位置。 這是使用 checkpointing and write ahead logs (檢查點(diǎn)和預(yù)寫入日志)來完成的。 您可以使用 checkpoint location (檢查點(diǎn)位置)配置查詢,并且查詢將保存所有進(jìn)度信息(即,每個(gè)觸發(fā)器中處理的偏移范圍)和正在運(yùn)行的 aggregates (聚合)(例如quick example中的 woed counts ) 到 checkpoint location (檢查點(diǎn)位置)。 此檢查點(diǎn)位置必須是 HDFS 兼容文件系統(tǒng)中的路徑,并且可以在starting a query時(shí)將其設(shè)置為DataStreamWriter 中的選項(xiàng)。
aggDF.writeStream.outputMode("complete").option("checkpointLocation","path/to/HDFS/dir").format("memory").start()
示例: 查看并運(yùn)行Scala/Java/Python/R示例。
Spark Summit 2016 Talk -深入 Structured Streaming
原文地址: http://spark.apachecn.org/docs/cn/2.2.0/structured-streaming-programming-guide.html
網(wǎng)頁(yè)地址: http://spark.apachecn.org/
github: https://github.com/apachecn/spark-doc-zh(覺得不錯(cuò)麻煩給個(gè) Star,謝謝!~)