大数据(9e)Flink时间语义WaterMark

本文介绍了Flink中时间语义的概念,重点讲解了Watermark的原理及其在乱序数据流中的作用。Watermark策略用于处理事件时间窗口,允许迟到的数据并确保最终的一致性。通过示例代码展示了如何设置和使用Watermark策略,并讨论了多并行度下Watermark的更新和传递。此外,还提供了本地开发环境的配置和测试结果。

1、时间语义

时间语义直译说明
Event Time时间时间事件发生的时间
Process Time处理时间事件发生后,进入到程序(窗口)

事 件 时 间 < 处 理 时 间 事件时间<处理时间 <

2、Watermark(水位线)

  • Watermark,可译为水位线,用来标记水流的事件时间进度
  • Watermark作为数据流的一部分在流动,并且携带一个时间戳t
    Watermark(t)表示流的事件时间已经到达t,后续数据的事件时间应当大于t

有序流中的水印

乱序流中的水印

W(t)表示流的事件时间已经到达t,后续不应出现时间时间小于t的数据

2.1、Watermark策略

2.2、Watermark与窗口

  1. WaterMark触发窗口计算
  2. 窗口允许迟到的数据
  3. 侧输出流(sideOutput):处理关窗之后到达的数据

  • W a t e r M a r k ≥ 窗 口 结 束 时 间 WaterMark \ge 窗口结束时间 WaterMark ,会触发窗口计算
  • 窗 口 结 束 时 间 < W a t e r M a r k < ( 窗 口 结 束 时 间 + 允 许 迟 到 时 间 ) 窗口结束时间 \lt WaterMark \lt (窗口结束时间 + 允许迟到时间) <WaterMark<(+),每进一条迟到的数据就计算一次
  • W a t e r M a r k ≥ ( 窗 口 结 束 时 间 + 允 许 迟 到 时 间 ) WaterMark \ge (窗口结束时间 + 允许迟到时间) WaterMark(+) ,会关闭窗口

2.3、多并行度下Watermark的更新和传递

从所有分区中取事件时间最小的WaterMark来广播WaterMark

3、Watermark代码

本地开发环境:WIN10+IDEA

3.1、pom.xml

<!-- 配置 -->
<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <flink.version>1.14.6</flink.version>
    <scala.binary.version>2.12</scala.binary.version>
    <slf4j.version>2.0.3</slf4j.version>
    <log4j.version>2.17.2</log4j.version>
</properties>
<!-- https://mvnrepository.com/ -->
<dependencies>
    <!-- Flink -->
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-java</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-streaming-java_${scala.binary.version}</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-clients_${scala.binary.version}</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-runtime-web_${scala.binary.version}</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <!-- 日志 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-to-slf4j</artifactId>
        <version>${log4j.version}</version>
    </dependency>
</dependencies>

3.2、log4j.properties

log4j.rootLogger=error, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

3.3、java

Watermark策略
forMonotonousTimestamps()是时间单调递增的水位线策略
相当于 forBoundedOutOfOrderness(Duration.ofSeconds(0))
import org.apache.flink.api.common.eventtime.SerializableTimestampAssigner;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.SourceFunction;
import org.apache.flink.streaming.api.functions.windowing.ProcessAllWindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;

import java.time.Duration;
import java.util.Scanner;

public class Hello {
    public static void main(String[] args) throws Exception {
        //创建执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        //设置并行度
        env.setParallelism(1);
        //Watermark策略(最大事件时间-5秒)
        WatermarkStrategy<Long> w = WatermarkStrategy
                .<Long>forBoundedOutOfOrderness(Duration.ofSeconds(5))
                .withTimestampAssigner((SerializableTimestampAssigner<Long>) (s, l) -> s * 1000L);
        //加入自定义数据源
        DataStreamSource<String> dss = env.addSource(new MySource());
        //################################### 逻辑 ########################################
        dss
                //String转Long
                .map(Long::valueOf)
                //确定时间水位线
                .assignTimestampsAndWatermarks(w)
                //事件时间滚动窗口
                .windowAll(TumblingEventTimeWindows.of(Time.seconds(1000)))
                //允许迟到
                .allowedLateness(Time.seconds(1))
                //窗口内数据处理
                .process(new ProcessAllWindowFunction<Long, String, TimeWindow>() {
                    @Override
                    public void process(Context context, Iterable<Long> in, Collector<String> out) {
                        //打印窗口范围
                        System.out.println(context.window().toString());
                        //在窗口内,收集元素
                        out.collect(String.valueOf(in));
                    }
                })
                //打印结果
                .print();
        //################################### 逻辑 ########################################
        env.execute();
    }

    public static class MySource implements SourceFunction<String> {
        public MySource() {}

        @Override
        public void run(SourceContext<String> sc) {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                String str = scanner.nextLine().trim();
                if (str.equals("STOP")) {break;}
                if (!str.equals("")) {sc.collect(str);}
            }
            scanner.close();
        }

        @Override
        public void cancel() {}
    }
}

3.4、测试结果

窗口大小1000秒,Watermark=事件时间-5秒,允许迟到1秒
绿色为秒数输入,白色为窗口结果输出

4、多并行度Watermark代码

import org.apache.flink.api.common.eventtime.SerializableTimestampAssigner;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.SourceFunction;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;

import java.time.Duration;
import java.util.Scanner;

public class Hello {
    public static void main(String[] args) throws Exception {
        //创建执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        //设置并行度
        env.setParallelism(2);
        //Watermark策略
        WatermarkStrategy<Long> w = WatermarkStrategy
                .<Long>forBoundedOutOfOrderness(Duration.ofSeconds(5))
                .withTimestampAssigner((SerializableTimestampAssigner<Long>) (s, l) -> s * 1000L);
        //加入自定义数据源
        DataStreamSource<String> dss = env.addSource(new MySource());
        //################################### 业务逻辑 ########################################
        dss
                //String转Long
                .map(Long::valueOf)
                //确定时间水位线
                .assignTimestampsAndWatermarks(w)
                //分区
                .keyBy(s -> s % 2)
                //事件时间滚动窗口
                .window(TumblingEventTimeWindows.of(Time.seconds(1000)))
                //窗口内数据处理
                .process(new ProcessWindowFunction<Long, String, Long, TimeWindow>() {
                    @Override
                    public void process(Long key, Context context, Iterable<Long> in, Collector<String> out) {
                        //打印当前分区和水位线
                        System.out.println("分区:" + key + ";水位线:" + context.currentWatermark());
                        //打印窗口范围
                        System.out.println(context.window().toString());
                        //在窗口内,收集元素
                        out.collect(String.valueOf(in));
                    }
                })
                //打印
                .print("输出");
        //################################### 业务逻辑 ########################################
        env.execute();
    }

    public static class MySource implements SourceFunction<String> {
        public MySource() {}

        @Override
        public void run(SourceContext<String> sc) {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                String str = scanner.nextLine().trim();
                if (str.equals("STOP")) {break;}
                if (!str.equals("")) {sc.collect(str);}
            }
            scanner.close();
        }

        @Override
        public void cancel() {}
    }
}

测试结果(并行度=2)

5、Appendix

en🔉cn
watermarkˈwɔːtərmɑːrkn. 水印;水位标志;vt. 在……上印水印(图案)
gapɡæpn. 缝隙,缺口;(时间上的)间隔
tumblingˈtʌmblɪŋn. 翻腾运动
tumbleˈtʌmblv. 翻滚;n. 跌倒,滚落;(数量、价值)暴跌;混乱,杂乱;翻跟头
in order按顺序
out of order次序颠倒
boundedbaʊndɪdadj. 有界限的
assignəˈsaɪnv. 布置(任务);分配(某物);派遣;确定(价值、功能、时间、地点)
monotonousməˈnɑːtənəsadj. 单调乏味的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小基基o_O

您的鼓励是我创作的巨大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值