说明
- 之前在引入StructuredStreaming的时候提到
- StructuredStreaming可以基于事件时间做延迟数据的处理,
- 那么接下来进行原来说明和代码演示
时间分类
- 事件时间:event-time:表示数据/数据真正发生的时间–现在用 因为它才能真正反映数据的真实状态
- 处理时间:process-time:表示数据被处理时的时间–以前用
- 摄入时间:Ingestion-time:表示数据到达系统的数据–不用
- 理解
- 现在假设,你正在去往地下停车场的路上,并且打算用手机点一份外卖。选好了外卖后,你就用在线支付功能付款了,这个时候是11点55分。恰好这时,你走进了地下停车库,而这里并没有手机信号。因此外卖的在线支付并没有立刻成功,而支付系统一直在Retry重试“支付”这个操作。
- 当你找到自己的车并且开出地下停车场的时候,已经是12点05分了。这个时候手机重新有了信号,手机上的支付数据成功发到了外卖在线支付系统,支付完成。
- 在上面这个场景中你可以看到,
支付数据的事件时间是11点55分,而支付数据的处理时间是12点05分
- 也就是上面的案例中
- 事件时间:event-time,事情
真正发生的时间
为 11点55分 - 处理时间:process-time,表示事件/数据
被系统处理的时间
为12点05分 - 而如果要计算当天上午的订单12点之前的订单,那么就应该考虑使用订单的事件时间来统计
- 而为了完成这个目标, 之前的技术是不行的得使用Dataflow模型-
- 事件时间:event-time,事情
实际需求
- 到达窗口的最大的事件时间 - 允许延迟的时间 >= 窗口原来的结束时间 就触发该窗口的计算!
- 否则该窗口就一直等待数据到来(这样的话不就给了迟到的数据一些机会)
API演示
package cn.hanjiaxiaozhi.structedstream
import java.sql.Timestamp
import org.apache.spark.SparkContext
import org.apache.spark.sql.{
DataFrame, SparkSession}
/**
* Author hanjiaxiaozhi
* Date 2020/7/26 16:35
* Desc 演示StructuredStreaming中的基于事件时间的延迟数据处理
* 注意这里仅仅演示API,实际中使用都是用Flink中的,因为Flink的该功能更加的完善和强大
*/
object WordCountWithWindowAndWatermakerAndEventTime {
def main(args: Array[String]): Unit = {
//1.准备StructuredStreaming执行环境
val spark: SparkSession = SparkSession.builder.appName("wc").master("local[*]").getOrCreate()
val sc: SparkContext = spark.sparkContext
sc.setLogLevel("WARN")
//2.读取node01:9999端口的数据
val df: DataFrame = spark.readStream//表示使用DataFrame/DataSet做流处理并加载数据
.option("host", "node01")//指定ip
.option("port", 9999)//指定端口
.option("includeTimeStamp",true)//表示将从socket接收到的数据的时间作为事件时间,演示后面的API,Socket不好模拟延迟
.format("socket")//指定数据源为socket
.load()//开始加载数据
//3.做WordCount
import org.apache.spark.sql.functions._
import spark.implicits._
// Dataset[(单词, 时间戳-事件时间)]
val wordAndTimeDF: DataFrame = df.as[(String, Timestamp)] //给DF添加上类型并转为DS
.flatMap(line => {
line._1.split(" ").map((_, line._2))
}).toDF("word","timestamp")
//4.基于事件时间进行窗口聚合并设置最大允许的延迟时间
val result = wordAndTimeDF
//以下为关键代码
//到达窗口的最大的事件时间 - 允许延迟的时间 >= 窗口原来的结束时间 就触发该窗口的计算!
// ===========================
.withWatermark("timestamp", "5 seconds")//指定事件时间是哪一列,并设置最大允许的延迟时间为5s
// ===========================
.groupBy(//分组时指定时间列,和窗口长度和滑动间隔,以及分组字段
window($"timestamp", "10 seconds", "5 seconds"), $"word")
.count()//聚合/计算
.sort("window")//按照窗口时间进行排序
//5.输出结果
result.writeStream
.format("console")//指定往控制台输出
.outputMode("complete")//输出模式,complete表示每次将所有数据都输出,必须包含聚合
.option("truncate",false)
.start()//开启
.awaitTermination()//等待结束
}
}