1、背景

如果多并行度,其实针对不同分区的Watermark,会取最小值

2、代码

public class WatermarkLateDemo {

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);


        SingleOutputStreamOperator<WaterSensor> sourceStream = env
                .socketTextStream("localhost", 7777)
                .map(new WaterSensorMapFunction());

        WatermarkStrategy<WaterSensor> watermarkStrategy = WatermarkStrategy
                .<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(3)) // 这里是最大乱序程度,其实就是认为最大乱序是3s
                .withTimestampAssigner(((element, recordTimestamp) -> element.getTs() * 1000L)); // 周期性的从数据中获取时间

        SingleOutputStreamOperator<WaterSensor> sensorWithWatermark = sourceStream.assignTimestampsAndWatermarks(watermarkStrategy);

        OutputTag<WaterSensor> lateTag = new OutputTag<>("late-data", Types.POJO(WaterSensor.class));

        SingleOutputStreamOperator<String> processStream = sensorWithWatermark.keyBy(sensor -> sensor.getId())
                .window(TumblingEventTimeWindows.of(Time.seconds(10)))
                .allowedLateness(Time.seconds(2)) // 推迟2s关窗
                .sideOutputLateData(lateTag)
                .process(new ProcessWindowFunction<WaterSensor, String, String, TimeWindow>() {
                    @Override
                    public void process(String key, ProcessWindowFunction<WaterSensor, String, String, TimeWindow>.Context context, Iterable<WaterSensor> elements, Collector<String> out) throws Exception {
                        long startTs = context.window().getStart();
                        long endTs = context.window().getEnd();
                        String windowStart = DateFormatUtils.format(startTs, "yyyy-MM-dd HH:mm:ss.SSS");
                        String windowEnd = DateFormatUtils.format(endTs, "yyyy-MM-dd HH:mm:ss.SSS");

                        long count = elements.spliterator().estimateSize();

                        out.collect("key=" + key + "的窗口[" + windowStart + "," + windowEnd + "]包含" + count + "条数据===>" + elements.toString());
                    }
                });

        processStream.print();
        // 从流获取侧输出流,打印
        processStream.getSideOutput(lateTag).printToErr("关闭后的迟到数据");

        env.execute();
    }
}

事件时间针对迟到数据
1、设置乱序程度 forBoundedOutOfOrderness
2、设置迟到,窗口关闭时间 allowedLateness
3、设置侧输出流 sideOutputLateData

输入数据

qiaozhanwei@192 ~ % nc -lk 7777
s1,1,1
s2,5,5
s1,10,10
s1,13,5
s1,2,2
s1,15,15
s1,3,3
s1,4,4

输出

key=s1的窗口[1970-01-01 08:00:00.000,1970-01-01 08:00:10.000]包含1条数据===>[WaterSensor{id='s1', ts=1, vc=1}]
key=s2的窗口[1970-01-01 08:00:00.000,1970-01-01 08:00:10.000]包含1条数据===>[WaterSensor{id='s2', ts=5, vc=5}]
key=s1的窗口[1970-01-01 08:00:00.000,1970-01-01 08:00:10.000]包含2条数据===>[WaterSensor{id='s1', ts=1, vc=1}, WaterSensor{id='s1', ts=2, vc=2}]
关闭后的迟到数据> WaterSensor{id='s1', ts=3, vc=3}
关闭后的迟到数据> WaterSensor{id='s1', ts=4, vc=4}

3、分析

当 Apache Flink 的全局并行度设置为 1 时,所有的数据处理都会在一个单独的任务中进行,即整个作业只有一个并行子任务来处理所有的 key。在这种情况下,不同 key 的 Watermark 推进方式如下:

3.1、全局并行度为 1 的 Watermark 推进机制

  • 单个 Watermark 管理:由于所有的数据都在同一个并行子任务中处理,整个作业中只有一个全局的 Watermark。在 Flink 中,Watermark 是用来标识事件时间的进度。在并行度为 1 时,无论数据中有多少不同的 key,都只有一个 Watermark 在全局控制事件时间的推进
  • Watermark 统一推进:所有 key 的事件会共享同一个 Watermark,也就是说,来自不同 key 的事件时间不会独立推进,而是整个数据流中最小的事件时间决定 Watermark 的推进。这意味着 Flink 的 Watermark 机制在这种情况下是基于所有 key 中的最小时间戳来前进的

3.2、具体 Watermark 推进逻辑

在并行度为 1 的情况下:

  • 所有 key 的数据都会在同一个任务中被处理,这意味着 Flink 只会维护一个 Watermark
  • Watermark 的值取决于这个任务接收到的所有数据中的最小事件时间戳。例如,如果任务收到的数据中有多个 key,并且这些 key 的事件时间分别是 T1、T2、T3,则 Watermark 将会取其中的最小值来更新

例子:
假设你有三个 key,key1、key2 和 key3,它们的事件时间分别是:

•    key1: 事件时间 1000ms
•    key2: 事件时间 1200ms
•    key3: 事件时间 800ms

在这种情况下,全局的 Watermark 将是 800ms,因为这是所有 key 中的最小事件时间

3.3、 Watermark 对窗口的影响

由于 Flink 的窗口操作(如滚动窗口、滑动窗口等)依赖于 Watermark 来触发窗口的计算,当并行度为 1 时:

  • 不同 key 的事件仍然可以被分组到不同的窗口中,依据事件时间分配窗口。
  • 但窗口计算的触发是基于全局 Watermark 的。这意味着,只有当全局 Watermark 超过某个窗口的结束时间时,Flink 才会触发该窗口的计算

如果有某些 key 的事件时间较晚到达,它们会延迟 Watermark 的推进,从而延迟所有 key 的窗口计算


journey
32 声望20 粉丝