Flink水位线之watermark原理及实战

本文详细介绍了Flink中watermark的概念,如何通过watermark机制处理实时流处理中的乱序事件,以及一个实际的代码示例,展示了如何设置watermark并结合window来确保数据的准确性和时效性。

watermark概念

Flink 实际上是用 watermarks来实现 Event - Time 的功能。watermark在Flink中也属于特殊事件,其精髓在于当某个运算值收到 。带有时间戳“T”的watermarks时就意味着它不会接收到新的数据了。

使用watermark的好处在于可以准确预估收到数据的截止时间。举例,假设预期收到数据时间与输出结果时间的时间差延迟 5分钟,那么Flink 中所有的windows Operator 搜索 3点至4点的数据,但因为存在延迟需要再多等5 分钟直至收集完4:05分的数据,此时方能判定4点钟的资料收集完成了,然后才会产出3点至4点的数据结果。这个时间段的结果对应的就是watermarks 的部分。

watermark作用

    watermark是用于处理乱序事件的,而正确的处理乱序事件,通常用watermark机制结合window来实现。

    我们知道,流处理从事件产生,到流经source,再到operator,中间是有一个过程和时间的。虽然大部分情况下,流到operator的数据都是按照事件产生的时间顺序来的,但是也不排除由于网络、背压等原因,导致乱序的产生(out-of-order或者说late element)。

    但是对于late element,我们又不能无限期的等下去,必须要有个机制来保证一个特定的时间后,必须触发window去进行计算了。这个特别的机制,就是watermark。

watermark示

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);  //设置时间分配器

        env.setParallelism(1);  //设置并行度
        env.getConfig().setAutoWatermarkInterval(9000);//每9秒发出一个watermark

        DataStream<String> text = env.socketTextStream("localhost", 9900);

        DataStream<Tuple3<String, Long, Integer>> counts = text.filter(new FilterClass()).map(new LineSplitter())
                    .assignTimestampsAndWatermarks(new AssignerWithPeriodicWatermarks<Tuple3<String, Long, Integer>>() {
                    private long currentMaxTimestamp = 0l;
                    private final long maxOutOfOrderness = 10000l;   //这个控制失序已经延迟的度量
                    //获取EventTime
                    @Override
                    public long extractTimestamp(Tuple3<String, Long, Integer> element, long previousElementTimestamp) {
                        long timestamp = element.f1;
                        currentMaxTimestamp = Math.max(timestamp, currentMaxTimestamp);
                        System.out.println(
                                "get timestamp is " + timestamp + " currentMaxTimestamp " + currentMaxTimestamp);
                        return timestamp;
                    }
                    //获取Watermark
                    @Override
                    public Watermark getCurrentWatermark() {
                        System.out.println("wall clock is " + System.currentTimeMillis() + " new watermark "
                                + (currentMaxTimestamp - maxOutOfOrderness));
                        return new Watermark(currentMaxTimestamp - maxOutOfOrderness);
                    }
                }).keyBy(0).timeWindow(Time.seconds(5))
                // .allowedLateness(Time.seconds(10))
                .sum(2);
        counts.print();
        env.execute("Window WordCount");


图片

图片,1625970416002(2021-07-11 10:26:56),1625970418002(2021-07-11 10:26:58),1625970419002(2021-07-11 10:26:59),1625970453002(2021-07-11 10:27:33)

我们水位线设置的9秒,当时间超过9秒时,那么会触发计算,从而会有下面的输出.触发计算的时间点是

  1. watermark超过了window的endtime.

  2. 在该window中有数据.

只有同时满足这两个条件,就会触发计算.

如果觉得文章能帮到您,欢迎关注微信公众号:“蓝天Java大数据” ,共同进步!

 

### Flink 水位线机制的工作原理与实现方式 Flink水位线Watermark)机制是用于处理事件时间(Event Time)的核心组件之一,主要用于解决数据流中的乱序问题。以下是关于其工作原理和实现方式的详细说明: #### 1. 水位线的基本概念 水位线是一种时间标记,表示事件时间在数据流中的进展。它被用来跟踪流中每个元素的时间戳,并确保在一定延迟范围内能够正确处理乱序数据[^2]。例如,在引用中可以看到水位线的值随着数据流的变化而变化,如从 `-9223372036854775808` 到 `4999` 的递增过程[^1]。 #### 2. 水位线的生成策略 Flink 提供了多种生成水位线的策略,用户可以根据数据流的特点选择合适的方案。常见的策略包括: - **有序流**:如果数据流是完全有序的,可以直接使用单调递增的时间戳生成水位线。例如,`forMonotonousTimestamps()` 方法会为每个元素生成与其时间戳相等的水位线[^3]。 - **乱序流**:对于乱序数据流,可以设置允许的最大延迟时间(如 3 秒),通过 `forBoundedOutOfOrderness(Duration.ofSeconds(3))` 来定义水位线。这种情况下,水位线的值为当前观察到的最大时间戳减去允许的延迟时间[^3]。 #### 3. 水位线的传递机制 水位线会随着数据一起在任务间传递。当水位线到达某个算子时,该算子会将其内部的事件时钟更新为水位线的时间戳。如果算子需要将数据发送到下游任务,则会同时传递水位线和数据[^2]。例如,在直通式传输中,数据和水位线按照顺序依次传递和处理。 #### 4. 水位线与窗口操作的关系 水位线在窗口操作中起着至关重要的作用。只有当水位线超过窗口的结束时间时,窗口才会触发计算并输出结果。例如,在引用中提到的窗口 `[1553503185000, 1553503190000)` 中,水位线达到 `1553503190000` 后,窗口内的数据才会被处理[^4]。 #### 5. 处理乱序消息的实现 Flink 使用水位线机制来处理乱序消息。具体来说,系统会缓存那些时间戳小于当前水位线的事件,直到水位线足够高以触发窗口计算。例如,在引用中可以看到,尽管某些事件的时间戳较晚到达,但它们仍然被正确排序并处理[^4]。 ```python # 示例代码:定义水位线生成器 from pyflink.datastream import StreamExecutionEnvironment from pyflink.table import StreamTableEnvironment, EnvironmentSettings from pyflink.table.window import Tumble from pyflink.table.descriptors import Schema, OldCsv, FileSystem env = StreamExecutionEnvironment.get_execution_environment() t_env = StreamTableEnvironment.create(env, environment_settings=EnvironmentSettings.new_instance().in_streaming_mode().build()) # 定义水位线策略 t_env.connect(FileSystem().path('input')) .with_format(OldCsv() .field('key', 'String') .field('event_time', 'Timestamp(3)')) .with_schema(Schema() .field('key', 'STRING') .field('event_time', 'TIMESTAMP(3)') .watermark('event_time', 'SOURCE_WATERMARK()')) # 使用内置水位线 .register_table_source('source') # 注册表并执行窗口聚合 t_env.scan('source') \ .window(Tumble.over("5.seconds").on("event_time").alias("w")) \ .group_by("w, key") \ .select("key, COUNT(1) as cnt") \ .execute_insert('sink') ``` #### 6. 总结 Flink水位线机制通过时间标记的方式解决了事件时间语义下的乱序问题,确保了窗口计算的准确性和高效性。无论是有序流还是乱序流,都可以通过灵活的水位线生成策略来适应不同的应用场景。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值