前言
Flink 的 API 大体上可以划分为三个层次:最底层的 ProcessFunction、中间一层的 DataStream API 和最上层的 SQL / Table API,这三层的每一层都非常依赖于时间属性。
Flink 时间语义
Flink 支持的核心时间语义是 Processing Time 和 Event Time。两者的不同点如下表所示:
Processing Time(处理时间) | Event Time(事件时间) |
---|---|
真实世界的时间 | 数据世界的时间 |
处理数据节点的本地时间 | 数据本身携带的TimeStamp |
处理简单 | 处理复杂 |
结果不确定(无法重现) | 结果确定(可重现) |
在判断应该使用 Processing Time 还是 Event Time 时,可以遵循一个原则:当你的程序遇到某个问题要从上一个 checkpoint 或者 savepoint 进行重启时,是否希望结果完全相同。
- 希望结果完全相同,就只能用 Event Time。
- 如果接受结果不同,则可以用 Processing Time。
Processing Time 的一个常见的用途是,根据现实时间来统计整个系统的吞吐,比如要计算现实时间一个小时处理了多少条数据,这种情况只能使用 Processing Time。
注意:当使用每小时 Processing Time 时间窗口时,窗口包括在系统时钟指示整点之间的数据。例如:一个程序在 9:15 开始运行,则第一个每小时处理时间窗口将包括在 9:15 和 10:00 之间处理的事件,下一个窗口将包括在 10:00 和 11:00 之间处理的事件。
时间的特性
对于 Processing Time:因为使用的是本地机器节点的时间,所以每一次取到的 Processing Time 肯定都是递增的,相当于是一个有序的数据流。
对于 Event Time:因为时间是绑定在每一条数据上,由于网络延迟、程序内部逻辑等等原因,数据的时间可能会存在乱序的情况。
解决 Event Time 乱序的问题就需要使用 Watermark。
一个 Watermark 本质上就是一个 timestamp 数值,表示以后到来的数据已经再也没有小于或等于这个时间的了。
Watermark(t) 表示数据流中 “ event time =< t ” 的数据都已到达,流中不应该有 “ 时间戳 <= t ” 的数据。
事实上,Watermark(t) 发生之后,还会出现很多 “ 时间戳 <= t ” 的数据。这些数据称为 Lateness(迟到数据)。
Timestamp 和 Watermark 行为概览
1、Timestamp 分配和 Watermark 生成
WatermarkGenerator 是 Watermark 的生成器,接口代码如下:
// 可以基于事件或者周期性的生成 watermark
@Public
public interface WatermarkGenerator<T> {
// 每来一条数据调用一次,可检查或记录事件的时间戳,或基于事件本身去生成watermark
void onEvent(T event, long eventTimestamp, WatermarkOutput output);
// 周期性调用,可能会生成新的watermark,有可能不会。
// 不会生成watermark是因为上一个watermark还没有触发。
// 调用此方法生成watermark的间隔时间由ExecutionConfig.getAutoWatermarkInterval()决定。
void onPeriodicEmit(WatermarkOutput output);
}
1.1 Watemark 的生成方式有两种:
1、周期性生成
通常通过 onEvent() 观察传入的事件数据,然后定期调用 onPeriodicEmit() 发出 Watemark。
调用此方法生成 watermark 的间隔时间由 ExecutionConfig.getAutoWatermarkInterval() 决定。
当调用 onPeriodicEmit() 方法时,如果返回的 watermark 非空并值大于前一个 watermark,则将发出新的 watermark。
// 该 watermark 生成器可以覆盖的场景是:数据源在一定程度上乱序。
public class BoundedOutOfOrdernessGenerator implements WatermarkGenerator<MyEvent> {
private final long maxOutOfOrderness = 3500; // 3.5 秒
private long currentMaxTimestamp;
@Override
public void onEvent(MyEvent event, long eventTimestamp, WatermarkOutput output) {
currentMaxTimestamp = Math.max(currentMaxTimestamp, eventTimestamp);
}
@Override
public void onPeriodicEmit(WatermarkOutput output) {
// 发出的 watermark = 当前最大时间戳 - 最大乱序时间
output.emitWatermark(new Watermark(currentMaxTimestamp - maxOutOfOrderness - 1));
}
}
2、标记生成
查看 onEvent() 中的事件数据,并等待流中携带 Watermark 的特殊标记事件或打点数据。当获取到这些数据时,它就会发出 Watermark。
通常情况下,标记生成器不会通过 onPeriodicEmit() 方法发出 watermark。