Flink EventTime Window

Flink流计算传输中⽀持多种时间概念:ProcessingTime/EventTime/IngestionTime
在这里插入图片描述
如果Flink算⼦使⽤的时候不做特殊设定,默认使⽤的是ProcessingTime。其中和ProcessingTime类似IngestionTime都是由系统⾃动产⽣,不同的是IngestionTime是由DataSource源产⽣⽽ProcessingTime由计算算⼦产⽣。因此以上两种时间策略都不能很好的表达在流计算中事件产⽣时间(考虑⽹络传输延迟)。

Flink中⽀持基于EventTime语义的窗⼝计算,Flink会使⽤Watermarker机制去衡量事件时间推进进度。Watermarker会做为数据流的⼀部分随着数据⽽流动。Watermarker包含有⼀个时间t,这就表明流中不会再有事件时间t’<=t的元素存在。

Watermarker(t)= Max event time seen by Procee Node - MaxAllowOrderless

Watermarker
在Flink中常⻅的⽔位线的计算⽅式:

  • 固定频次计算⽔位线(推荐):
class UserDefineAssignerWithPeriodicWatermarks extends
AssignerWithPeriodicWatermarks[(String,Long)] {
 var maxAllowOrderness=2000L
 var maxSeenEventTime= 0L //不可以取Long.MinValue
 var sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
 //系统定期的调⽤ 计算当前的⽔位线的值
 override def getCurrentWatermark: Watermark = {
 new Watermark(maxSeenEventTime-maxAllowOrderness)
 }
 //更新⽔位线的值,同时提取EventTime
 override def extractTimestamp(element: (String, Long), previousElementTimestamp:
Long): Long = {
 //始终将最⼤的时间返回
 maxSeenEventTime=Math.max(maxSeenEventTime,element._2)
 println("ET:"+(element._1,sdf.format(element._2))+"
WM:"+sdf.format(maxSeenEventTime-maxAllowOrderness))
 element._2
  • Per Event 计算⽔位线 (不推荐):
class UserDefineAssignerWithPunctuatedWatermarks extends
AssignerWithPunctuatedWatermarks[(String,Long)] {
 var maxAllowOrderness=2000L
 var maxSeenEventTime= Long.MinValue
 var sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
 //每接收⼀条记录系统计算⼀次
 override def checkAndGetNextWatermark(lastElement: (String, Long),
extractedTimestamp: Long): Watermark = {
 maxSeenEventTime=Math.max(maxSeenEventTime,lastElement._2)
 println("ET:"+(lastElement._1,sdf.format(lastElement._2))+"
WM:"+sdf.format(maxSeenEventTime-maxAllowOrderness))
 new Watermark(maxSeenEventTime-maxAllowOrderness)
 }
 override def extractTimestamp(element: (String, Long), previousElementTimestamp:
Long): Long = {
 //始终将最⼤的时间返回
 element._2
 }
}
测试案列
object FlinkEventTimeTumblingWindow {
 def main(args: Array[String]): Unit = {
 val env = StreamExecutionEnvironment.getExecutionEnvironment
 env.setParallelism(1)//⽅便测试将并⾏度设置为 1
 //默认时间特性是ProcessingTime,需要设置为EventTime
 env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
 //设置定期调⽤⽔位线频次 1s
 // env.getConfig.setAutoWatermarkInterval(1000)
 //字符 时间戳
 env.socketTextStream("CentOS", 9999)
 .map(line=>line.split("\\s+"))
 .map(ts=>(ts(0),ts(1).toLong))
 .assignTimestampsAndWatermarks(new
UserDefineAssignerWithPunctuatedWatermarks)
 .windowAll(TumblingEventTimeWindows.of(Time.seconds(2)))
 .apply(new UserDefineAllWindowFucntion)
 .print("输出")
  env.execute("Tumbling Event Time Window Stream")
   }
}

注意:当流中存在多个Watermarker的时候,取最⼩值作为⽔位线。

迟到数据

在Flink中,⽔位线⼀旦没过窗⼝的EndTime,这个时候如果还有数据落⼊到已经被⽔位线淹没的窗⼝,我定义该数据为迟到的数据。这些数据在Spark是没法进⾏任何处理的。在Flink中⽤户可以定义窗⼝元素的迟到时间t’。

  • 如果Watermarker时间t < 窗⼝EndTime t’’ + t’ 则该数据还可以参与窗⼝计算。
object FlinkEventTimeTumblingWindowLateData {
 def main(args: Array[String]): Unit = {
 val env = StreamExecutionEnvironment.getExecutionEnvironment
 env.setParallelism(1)//⽅便测试将并⾏度设置为 1
 //默认时间特性是ProcessingTime,需要设置为EventTime
 env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
 //设置定期调⽤⽔位线频次 1s
 // env.getConfig.setAutoWatermarkInterval(1000)
 //字符 时间戳
 env.socketTextStream("CentOS", 9999)
 .map(line=>line.split("\\s+"))
 .map(ts=>(ts(0),ts(1).toLong))
 .assignTimestampsAndWatermarks(new
UserDefineAssignerWithPunctuatedWatermarks)
 .windowAll(TumblingEventTimeWindows.of(Time.seconds(2)))
 .allowedLateness(Time.seconds(2))
 .apply(new UserDefineAllWindowFucntion)
 .print("输出")
 env.execute("Tumbling Event Time Window Stream")
 }
}
  • 如果Watermarker时间t >= 窗⼝EndTime t’’ + t’ 则该数据默认情况下Flink会丢弃。当然⽤户可以将too late数据通过side out输出获取,⼀遍⽤户知道哪些迟到的数据没能加⼊正常计算。
object FlinkEventTimeTumblingWindowTooLateData {
 def main(args: Array[String]): Unit = {
 val env = StreamExecutionEnvironment.getExecutionEnvironment
 env.setParallelism(1)//⽅便测试将并⾏度设置为 1
 //默认时间特性是ProcessingTime,需要设置为EventTime
 env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
 //设置定期调⽤⽔位线频次 1s
 // env.getConfig.setAutoWatermarkInterval(1000)
 val lateTag= new OutputTag[(String,Long)]("late")
 //字符 时间戳
 var result=env.socketTextStream("CentOS", 9999)
 .map(line=>line.split("\\s+"))
 .map(ts=>(ts(0),ts(1).toLong))
 .assignTimestampsAndWatermarks(new
UserDefineAssignerWithPunctuatedWatermarks)
 .windowAll(TumblingEventTimeWindows.of(Time.seconds(2)))
 .allowedLateness(Time.seconds(2))
 .sideOutputLateData(lateTag)
 .apply(new UserDefineAllWindowFucntion)
 result.print("正常")
 result.getSideOutput(lateTag).printToErr("太迟")
 env.execute("Tumbling Event Time Window Stream")
 }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值