记录一下关于flink窗口or定时器的问题

最近在写一个需要用到定时器的需求,结果遇到了问题。

有时候定时器可以正常触发,有时候却迟迟无法触发,明明已经过去很久时间了,程序却一直没有进入ontimer方法。

针对这种玄学问题,经过上网搜索以及添加日志打印,发现是水位线的问题。

在这里我们先回顾一下水位线的生成规则

Flink 的水位线是基于数据的事件时间生成的。如果某条数据的事件时间远远大于当前水位线,默认情况下,水位线会被立即推到该未来时间。例如:

  • 当前水位线为 2023-10-01 10:00:00

  • 一条数据的事件时间为 2062-01-01 00:00:00

  • 水位线会直接更新到 2062-01-01 00:00:00

这意味着:

  • 所有后续数据的事件时间如果小于 2062-01-01 00:00:00,会被认为是“迟到数据”,默认被丢弃(除非设置了允许延迟)。

  • 窗口计算会立即触发(如果窗口的结束时间 <= 当前水位线),可能导致错误的结果。

以下是一个验证方案

// 示例数据
DataStream<Event> input = env.fromElements(
    new Event("A", 1667200000000L), // 2022-11-01 00:00:00
    new Event("B", 32503680000000L) // 2062-01-01 00:00:00
);

input.assignTimestampsAndWatermarks(
    WatermarkStrategy
        .<Event>forMonotonousTimestamps() // 默认单调递增水位线
        .withTimestampAssigner((event, ts) -> event.getEventTime())
);

// 观察 Watermark 输出
input.process(new ProcessFunction<Event, Event>() {
    @Override
    public void processElement(Event event, Context ctx, Collector<Event> out) {
        long watermark = ctx.timerService().currentWatermark();
        System.out.println("Current Watermark: " + new Date(watermark));
        out.collect(event);
    }
});

输出结果

Current Watermark: -9223372036854775808 (初始值)
Current Watermark: 2062-01-01 00:00:00 (被推到了未来)

而无论是窗口还是定时器的计算,都是基于水位线去触发的。所以如果你的乱序时间不大,可以设置一个合理的乱序时间来处理数据。

像博主这样的,源端数据中有2062年数据的,只能过滤掉这种脏数据或者对数据进行另外的处理了。

以下提供一些解决手段

 (1) 数据清洗:过滤或修正异常时间戳

在数据进入 Flink 作业前,通过 ProcessFunction 或 Filter 过滤掉未来的时间戳:

DataStream<Event> cleanedStream = inputStream
    .filter(event -> event.getEventTime() < FUTURE_THRESHOLD);
(2) 限制水位线增长:自定义 Watermark 生成器

通过 WatermarkStrategy 限制水位线增长,避免被未来时间戳推得太远:

WatermarkStrategy<Event> strategy = WatermarkStrategy
    .<Event>forBoundedOutOfOrderness(Duration.ofDays(1)) // 允许1天延迟
    .withTimestampAssigner((event, timestamp) -> event.getEventTime())
    .withIdleness(Duration.ofMinutes(5)) // 防止空闲分区问题
    .withWatermarkAlignment("alignment-group-1", Duration.ofHours(1)); // Flink 1.17+ 特性
(3) 使用侧输出流处理异常数据

将异常时间戳的数据路由到侧输出流,单独处理:

OutputTag<Event> futureTimeTag = new OutputTag<>("future-time") {};

SingleOutputStreamOperator<Event> mainStream = inputStream
    .process(new ProcessFunction<Event, Event>() {
        @Override
        public void processElement(Event event, Context ctx, Collector<Event> out) {
            if (event.getEventTime() > MAX_VALID_TIMESTAMP) {
                ctx.output(futureTimeTag, event); // 输出到侧流
            } else {
                out.collect(event);
            }
        }
    });

// 获取异常数据流
DataStream<Event> futureTimeStream = mainStream.getSideOutput(futureTimeTag);

总结

  • 默认情况下,Flink 会信任数据的事件时间,若某条数据的事件时间是未来时间,水位线会被推到该时间。

  • 必须主动处理异常时间戳(如清洗、限制水位线、侧输出流),否则会导致作业逻辑错误。

  • 建议在生产环境中始终对事件时间做合理性校验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值