Flink的时间和窗口
Flink中的时间
及时流处理 是有状态流处理的扩展,实现及时流处理的 时间 起到了很大的作用。
在Flink的时间概念中主要分为下面两种:
事件时间: 事件时间是每个单独事件在其生产设备上发生的时间。
处理时间: 处理时间是指执行相应操作的机器的系统时间。
在Flink中为了衡量事件时间的进度,引入了 watermark 机制。
watermark 将作为数据流的一部分流动,而且带有时间戳的属性,如此在 乱序流 中,我们就可以判断出哪些事件是迟到的(即后面到达的事件的时间戳小于此时的水印)。
watermark的生成
在java中想要分配时间戳和水印需要用到 assignTimestampsAndWatermarks() 方法:
// 有序流生成watermark
source.assignTimestampsAndWatermarks(WatermarkStrategy
.<String>forMonotonousTimestamps()
.withTimestampAssigner(new SerializableTimestampAssigner<String>() { // 得到时间戳
@Override
public long extractTimestamp(String s, long l) {
// 我这里是字符串,字符串的格式为 timestamp msg
// 当然你也可以有自己的POJO类,并且里面有timestamp这个属性,你就可以直接返回该属性了
return Long.parseLong(s.split(" ")[0]); // 返回时间戳,单位是毫秒
}
}));
// 无序流生成watermark
source.assignTimestampsAndWatermarks(WatermarkStrategy
.<String>forBoundedOutOfOrderness(
Duration.ofSeconds(2)) // 允许迟到时间2秒
.withTimestampAssigner(new SerializableTimestampAssigner<String>() { // 得到时间戳
@Override
public long extractTimestamp(String s, long l) {
// 我这里是字符串,字符串的格式为 timestamp msg
// 当然你也可以有自己的POJO类,并且里面有timestamp这个属性,你就可以直接返回该属性了
return Long.parseLong(s.split(" ")[0]); // 返回时间戳,单位是毫秒
}
}));
Flink中的窗口
聚合事件(例如,计数、总和)在流上的工作方式与批处理不同。因为流是不断的,不可能将流中的所有元素进行计算。
流上的聚合(计数、总和等)由窗口限定,
例如 “过去 5 分钟的计数”或“最后 100 个元素的总和” 都算是一种窗口,只不过前者是 时间驱动 的后者是 数据驱动 的。
上面的分类只是大致的,而按照不同的分类窗口划分为以下几类:
滚动窗口: 将每个元素分配给指定窗口大小的窗口。滚动窗口具有固定大小并且不重叠。
滑动窗口: 将元素分配给固定长度的窗口。类似滚动窗口,但是允许重叠(一个元素分配给多个窗口)
会话窗口: 按活动会话对元素进行分组。会话窗口不重叠,没有固定的开始和结束时间。当会话窗口在一段时间内没有接收到元素时,即当出现不活动间隙时,会话窗口将关闭。
全局窗口: 将具有相同键的所有元素分配给同一个全局窗口 。全局窗口没有自然结束,若不指定自定义触发器则不会计算。
更详细的可以看官方窗口文档
窗口函数
在定义了窗口分配器之后,我们需要使用 window function 指定我们想要在每个窗口上执行的计算。
在流上使用窗口的步骤大致如下:
source
.assignTimestampsAndWatermarks(WatermarkStrategy // 设置watermark
// Event自定义类,其中有Timestamp类型属性timestamp
.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(2)) // 乱序流、允许迟到2秒
// 指定时间戳
.withTimestampAssigner((SerializableTimestampAssigner<Event>) (event, l) -> event.getTimestamp().getTime())
)
.keyBy(Event::getUser) // 按Event的user属性分组
.window(
// 参数:窗口长度,偏移量
TumblingEventTimeWindows.of(Time.hours(2), Time.hours(0)) // 2小时的滚动事件时间窗口
// 其它窗口
// 滑动 SlidingEventTimeWindows
// 会话 EventTimeSessionWindows
)
.maxBy("timestamp") // 简单用个max求最大时间戳举例
.print(); // sink输出
使用归约函数会有一个问题,就是气要求输入类型和输出类型一致,并不够灵活,于是在窗口函数中Flink还提供了聚合函数 AggregateFunction , 它有三个参数 输入类型(IN)、累加器类型(ACC)、输出类型(OUT) ,这样就使得整个流结果的输出更加灵活。