EventTime窗口的起始时间是以读取的第一条事件时间戳为基准,比方说
第一条时间时间是: 2020-11-11 14:43:11,
滚动时间间隔15s,
watermark间隔是3s,
偏移量是0,
延迟时间是1分钟后放入侧输出流
那么起始时间就是:
1605076991*1000 - (1605076991*1000 - 0 + 15*1000)% (15*1000)
1605076991000 - 11000 :减去11s
1605076980
也就是说窗口的起始时间是从 :2020-11-11 14:43:00 开始第一个窗口,那么窗口按照15s一个窗口进行切分,
窗口 value
1)[14:43:00~14:43:15) 2
2)[14:43:15~14:43:30) 3
3)[14:43:30~14:43:45) 4 5 6
4)[14:43:45~14:44:00) 7
5)[14:44:00~14:44:15) 8 9
6) [14:44:15~ 14:44:30) 10 11 12 13
...
测试数据 对应时间 watermark 状态
s1,1605076991,2 14:43:11 14:43:08 第一条数据进来,开启第一个窗口
s1,1605076998,3 14:43:18 14:43:15 wm是15,第一个窗口关闭,最小值是2
s1,1605077010,4 14:43:30 14:43:27
s1,1605077011,5 14:43:31 14:43:28
s1,1605077019,6 14:43:39 14:43:36 wm是36,第二个窗口关闭,最小值是3
s1,1605077025,7 14:43:45 14:43:42
s1,1605077040,8 14:44:00 14:43:57 第三个窗口关闭,最小值是4
s1,1605077054,9 14:44:14 14:44:11 第四个窗口关闭,最小值是7
s1,1605077055,10 14:44:15 14:44:12
s1,1605077056,11 14:44:16 14:44:13
s1,1605077057,12 14:44:17 14:44:14
s1,1605077058,13 14:44:18 14:44:15 第五个窗口关闭,最小值是8
s1,1605076990,1 14:43:10 14:43:07 进入侧输出流
s1,1605081170,100 15:52:50 15:52:43 因为时间差比较大,之前所有的窗口全部关闭,最小值是10
s1,1605076989,11 14:43:09 14:43:06 进入侧输出流
代码如下:
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time
/**
* FileName: WindowT
* Author: ***********
* Date: 2020/11/11 11:14
* Description:
*/
object WindowT {
def main(args: Array[String]): Unit = {
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
//设置周期性插入watermark,默认时间间隔是200s,也可以通过下面设置时间间隔
env.getConfig.setAutoWatermarkInterval(50)
//读取socket流数据
val inputStream: DataStream[String] = env.socketTextStream("node01", 7777)
//转换数据 并提取时间戳
val dataStream: DataStream[SensorReading] = inputStream.map(data => {
val arr: Array[String] = data.split(",")
SensorReading(arr(0), arr(1).toLong, arr(2).toDouble)
})
/**
* 提取时间戳的方式有两种,一种是这种升序时间戳,另外一种是乱序的时间戳
*/
// 1.方式1 提取升序的时间戳(毫秒)
//.assignAscendingTimestamps(_.timestamp * 1000L)
// 2.方式2 通过周期性提取有界的乱序时间戳,也就是乱序时间戳
// 给了3s的watermark
.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[SensorReading](Time.seconds(3)) {
override def extractTimestamp(element: SensorReading): Long = element.timestamp * 1000L
})
//侧输出流
val latetag = new OutputTag[(String, Double, Long)]("late")
//每15秒统计一次,窗口内各传感器所有温度的最小值,以及最新的时间戳
val resultStream: DataStream[(String, Double, Long)] = dataStream.map(data => (data.id, data.temperature, data.timestamp))
.keyBy(0)
//滚动窗口 15s
.timeWindow(Time.seconds(15))
//为保证延迟数据不丢失,设置处理迟到数据以及从侧输出流获取数据
//延迟1分钟
.allowedLateness(Time.minutes(1))
//获取测输出流
.sideOutputLateData(latetag)
.reduce((curData, newData) => (curData._1, curData._2.min(newData._2), newData._3))
// 打印侧输出流
resultStream.getSideOutput(latetag).print("late")
//打印结果
resultStream.print("result")
//执行程序
env.execute("window demo 2")
}
}
下面看timeWindow源码