Structured Streaming
简介
Structured Streaming 构建在SparkSQL之上的流处理引擎。可以使用户继续使用DataSet/dataFrame操作流数据。并且提供了多种计算模型可供选择,默认情况下,使用的依然是Spark的marco batch这种计算模型能够到100ms左右的end-to-end的精准一次的容错计算。除此之外也提供了基于EventTime 语义的窗口计算(DStream 基于Processor Time不同)。同时在spark-2.3版本又提出新的计算模型Continuous Processing
可以达到1ms左右的精准一次的容错计算。
快速入门案例
使用Structured Streaming实现通过接收SocketServer发送过来的文本数据,实现wordcount。
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>2.4.3</version>
</dependency>
//1.构建SparkSession
val spark=SparkSession.builder()
.appName("wordcount")
.master("local[6]")
.getOrCreate()
import spark.implicits._
//2.创建输入流-readStream
var lines= spark.readStream
.format("socket")
.option("host","CentOS")
.option("port",9999)
.load()
//3.对dataframe实现转换
var wordCounts = lines.as[String]
.f
//4.构建query 输出
val query = wordCounts.writeStream
.format("console")
.outputMode(OutputMode.Update())//有状态持续计算 Complete| Update
.start()
//5.等待流结束
query.awaitTermination()
程序结构
1.构建SparkSession 对象
2.借助于SparkSession#readStream加载动态的Dataframe
3.使用dataframe API或者是SQL语句 实现对动态数据计算
4.通过DataFrame#writeStream方法构建StreamQuery对象
5.调用StreamQuery#awaitTermination等待关闭指令
基本概念
Structure Stream的核心思想是通过将实时数据流看成是一个持续插入table.因此用户就可以使用SQL查询DynamicTable|UnboundedTable。底层Spark通过StreamQuery实现对数据持续计算。
当对Input执行转换的时候系统产生一张结果表ResultTable
,当有新的数据产生的时候,系统会往Input Table
插入一行数据,这会最终导致系统更新ResultTable
,每一次的更新系统将更新的数据写到外围系统-Sink.
Output
定义如何将Result写出到外围系统,目前Spark支持三种输出模式:
Complete Mode
- 整个ResultTable的数据会被写到外围系统。Update Mode
- 只会讲ResultTable中被更新的行,写到外围系统(spark-2.1.1
+支持)Append Mode
- 只有新数据插入ResultTable的时候,才会将结果输出。注意:这种模式只适用于被插入结果表的数据都是只读的情况下,才可以将输出模式定义为Append(查询当中不应该出现聚合算子,当然也有特例,例如流中声明watermarker)
由于Structure Streaming计算的特点,Spark会在内存当中存储程序计算中间状态用于生产结果表的数据,Spark并不会存储Input Table
的数据,一旦处理完成之后,读取的数据会被丢弃。整个聚合的过程无需用户干预(对比Storm,Storm状态管理需要将数据写到外围系统)。
故障容错
Structure Streaming通过checkpoint和write ahead log去记录每一次批处理的数据源的偏移量(区间),可以保证在失败的时候可以重复的读取数据源。其次Structure Streaming也提供了Sink的幂等写的特性(在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同),因此Structure Streaming实现**end-to-end exactly-once **语义的故障恢复。
Structure Streaming API
自Spark-2.0版本以后Dataframe/Dataset才可以处理有界数据和无界数据。Structure Streaming也是用SparkSession方式去创建Dataset/DataFrame ,同时所有Dataset/DataFrame 的操作保持和Spark SQL中Dataset/DataFrame 一致。因此我们不在针对Dataset/DataFrame 的API和SQL展开讲解-略。
Input Sources
File Source
目前支持支持text, csv, json, orc, parquet等格式的文件,当这些数据被放入到采样目录,系统会以流的形式读取采样目录下的文件.
//1.创建SparkSession
val spark=SparkSession
.builder()
.master("local[6]")
.appName("printline")
.getOrCreate()
import spark.implicits._
var df=spark.readStream
.format("text")//json/csv/parquet/orc 等
.load("file:///D:/results/text")
//2 lisi true 28
var userDF=df.as[String].map(line=>line.split("\\s+"))
.map(tokens=>(tokens(0).toInt,tokens(1),tokens(2).toBoolean,tokens(3).toInt))
.toDF("id","name","sex","age")
val query = userDF.writeStream.format("console")
.outputMode(OutputMode.Append())
.start()
query.awaitTermination()
Socket source(debug)
//1.构建SparkSession
val spark=SparkSession.builder()
.appName("wordcount")
.master("local[6]")
.getOrCreate()
import spark.implicits._
//2.创建输入流-readStream DynamicTable
var lines= spark.readStream
.format("socket")
.option("host","CentOS")
.option("port",9999)
.load()
//3.对dataframe实现转换 - ResultTable
var wordCounts = lines.as[String]
.flatMap(line=>line.split("\\s+"))
// .groupBy("value").count()
//4.构建query 输出
val query = wordCounts.writeStream
.format("console")
.outputMode(OutputMode.Append())//有状态持续计算 Complete| Update
.start()
//5.等待流结束
query.awaitTermination()
}