Flink 对迟到数据的处理主要通过以下机制实现:
waterMark和Window机制解决了流式数据的乱序问题,对于因为延迟而顺序有误的数据,可以根据eventTime进行业务处理,对于延迟的数据Flink也有自己的解决办法,
主要的办法是给定一个允许延迟的时间,在该时间范围内仍可以接受处理延迟数据
设置允许延迟的时间是通过allowedLateness(lateness: Time)设置
保存延迟数据则是通过sideOutputLateData(outputTag: OutputTag[T])保存
获取延迟数据是通过DataStream.getSideOutput(tag: OutputTag[X])获取
1. 允许延迟(Allowed Lateness)
通过 allowedLateness 设置窗口关闭后的容忍时间。在此时间内到达的数据仍可触发窗口计算:
windowedStream
.allowedLateness(Duration.ofMinutes(5))
.reduce(new MyReduceFunction());
2. 侧输出(Side Output)
超过容忍时间的数据可通过侧输出捕获:
OutputTag<T> lateDataTag = new OutputTag<>("late-data");
WindowedStream<T> windowed = input
.keyBy(...)
.window(...)
.allowedLateness(...)
.sideOutputLateData(lateDataTag);
DataStream<T> lateStream = windowed.getSideOutput(lateDataTag);
3. 水位线(Watermark)机制
水位线标记事件时间进度,其生成策略(如 BoundedOutOfOrderness)直接影响延迟容忍度:
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
env.getConfig().setAutoWatermarkInterval(1000);
DataStream<T> withTimestamps = input
.assignTimestampsAndWatermarks(
new BoundedOutOfOrdernessTimestampExtractor<T>(Duration.ofSeconds(10)) {
@Override
public long extractTimestamp(T element) {
return element.getEventTime();
}
});
4. 窗口状态清理
窗口触发后状态默认保留至 allowedLateness 结束,需注意避免状态无限增长:
windowedStream
.trigger(new MyTrigger()) // 自定义触发器
.evictor(new MyEvictor()); // 自定义清理器
处理流程示例
1. 水位线推进至窗口结束时间
2. 窗口初次计算结果输出
3. 允许延迟期内新数据触发更新计算
4. 延迟期结束后:
- 侧输出捕获迟到数据
- 清除窗口状态
注意事项
- 容忍时间过长可能导致状态存储压力
- 侧输出数据需单独定义处理逻辑
- 事件时间与处理时间需明确区分
通过组合水位线、允许延迟和侧输出机制,Flink 实现了对乱序数据的鲁棒性处理,保障计算结果的准确性。