该组件进一步降低了处理数据的延迟时间, 它实现了"有且仅有一次(Exectly Once)" 语义, 可以保证数据被精准消费.
Structured Streaming 基于 Spark SQl 引擎
, 是一个具有弹性和容错的流式处理引擎. 使用 Structure Streaming 处理流式计算的方式和使用批处理计算静态数据(表中的数据)的方式是一样的.
随着流数据的持续到达, Spark SQL 引擎持续不断的运行并得到最终的结果. 我们可以使用 Dataset/DataFrame API 来表达流的聚合, 事件-时间窗口(event-time windows), 流-批处理连接(stream-to-batch joins)等等. 这些计算都是运行在被优化过的 Spark SQL 引擎上. 最终, 通过 chekcpoin 和 WALs(Write-Ahead Logs), 系统保证end-to-end exactly-once
.
总之, Structured Streaming 提供了快速, 弹性, 容错, end-to-end exactly-once 的流处理, 而用户不需要对流进行推理(比如 spark streaming 中的流的各种转换).
默认情况下, 在内部, Structured Streaming 查询使用微批处理引擎(micro-batch processing engine)处理, 微批处理引擎把流数据当做一系列的小批job(small batch jobs ) 来处理. 所以, 延迟低至 100 毫秒, 从 Spark2.3, 引入了一个新的低延迟处理模型:Continuous Processing, 延迟低至 1 毫秒.
import org.apache.spark.sql.streaming.StreamingQuery
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}
object WordCount1 {
def main(args: Array[String]): Unit = {
// 1. 创建 SparkSession. 因为 ss 是基于 spark sql 引擎, 所以需要先创建 SparkSession
val spark: SparkSession = SparkSession
.builder()
.master("local[*]")
.appName("WordCount1")
.getOrCreate()
import spark.implicits._
// 2. 从数据源(socket)中加载数据.
val lines: DataFrame = spark.readStream
.format("socket") // 设置数据源
.option("host", "hadoop201")
.option("port", 9999)
.load
// 3. 把每行数据切割成单词
val words: Dataset[String] = lines.as[String].flatMap(_.split("\\W"))
// 4. 计算 word count
val wordCounts: DataFrame = words.groupBy("value").count()
// 用sparksql计算
lines.as[String].flatMap(_.split("\\w")).createOrReplaceTempView("w")
val wordCount: sql.DataFrame = spark.sql(
"""
|select
| *,
| count(1) count
|from w
|group by count
|""".stripMargin)
// 5. 启动查询, 把结果打印到控制台
val query: StreamingQuery = wordCounts.writeStream
.outputMode("complete")
.format("console")
.start
query.awaitTermination()
spark.stop()
}
}
. DataFrame lines
表示一个"无界表(unbounded table)", 存储着流中所有的文本数据. 这个无界表包含列名为value
的一列数据, 数据的类型为String
, 而且在流式文本数据中的每一行(line)就变成了无界表中的的一行(row). 注意, 这时我们仅仅设置了转换操作, 还没有启动它, 所以现在还没有收到任何数据
2. 紧接着我们把 DateFrame 通过 .as[String]
变成了 DataSet, 所以我们可以切割每行为多个单词.得到的 words DataSet
包含了所有的单词.
3. 最后, 我们通过value
(每个唯一的单词)进行分组得到wordCounts DataFrame
, 并且统计每个单词的个数. 注意, wordCounts
是一个流式DataFrame
, 它表示流中正在运行的单词数(the running word counts of the stream
).
4. 我们必须在流式数据(streaming data)上启动查询. 剩下的实际就是开始接收数据和计算个数. 为此, 当数据更新的时候, 我们通过outputMode("complete")
来打印完整的计数集到控制台, 然后通过.start
来启动流式计算.
5. 代码执行之后, 流式计算将会在后台启动. 查询对象(query: StreamingQuery)可以激活流式查询(streaming query), 然后通过awaitTermination()
来等待查询的终止,从而阻止查询激活之后进程退出.