Watermark简介

在 Apache Flink 中,水印(Watermark) 是一种用于处理事件时间(Event Time)流数据的机制。它代表了流处理系统中对事件时间进度的理解,用来标识数据流中的时间点,从而帮助确定何时窗口应该关闭并触发计算。

水印的作用
控制窗口计算:Flink 使用水印来决定何时关闭基于事件时间的窗口。例如,在一个 10 分钟的滚动窗口中,只有当水印超过窗口结束时间时,Flink 才会认为该窗口内的所有数据都已经到达,并且可以安全地进行计算。
处理迟到数据:通过设置允许的最大延迟(Allowed Lateness),Flink 可以接收那些晚于预期到达的数据。水印使得我们可以定义如何处理这些迟到的数据,比如将它们添加到已经计算过的窗口中或忽略它们。

水印的工作原理
水印是按照事件时间戳生成的特殊标记,通常表示“在这个时间之前的所有事件都已经到达”。具体来说:

事件时间戳:每个事件都有一个时间戳,这个时间戳反映了事件实际发生的时间,而不是它被处理的时间。
水印生成器(Watermark Generator):这是负责生成水印的组件。它根据输入数据流中的事件时间戳来创建水印。最常见的是使用 forBoundedOutOfOrderness 方法来指定最大乱序程度,即允许的最大延迟时间。

推进水印:随着更多事件的到来,水印逐渐推进。理想情况下,水印应该是递增的,但实际应用中可能会有乱序的情况,因此我们需要设定一个合理的最大乱序时间。

WatermarkStrategy<OrderItem> watermarkStrategy = WatermarkStrategy
.<OrderItem>forBoundedOutOfOrderness(Duration.ofSeconds(5)) // 允许最多5秒的乱序
.withTimestampAssigner((event, timestamp) -> event.updateTime); // 提取事件时间戳的方法

水印的一个重要特性是它可以和允许的最大延迟一起工作,以便处理迟到的数据。例如,如果你设置了一个 1 小时的滚动窗口,并且允许最多 5 分钟的迟到数据,那么即使水印超过了窗口的结束时间,只要数据的事件时间不超过窗口结束时间加上 5 分钟,这些迟到的数据仍然会被包含在窗口计算中。

一、构造数据源

import org.apache.flink.streaming.api.functions.source.SourceFunction;

import java.util.Random;
import java.util.concurrent.TimeUnit; public class OrderItemSource implements SourceFunction<OrderItemSource.OrderItem> { private volatile boolean isRunning = true; @Override
public void run(SourceContext<OrderItem> sourceContext) throws Exception {
// 每一秒生生一条数据
for (int i=0; i < 100; i++) {
OrderItem orderItem = new OrderItem(i, i % 2, 1, "商品A", new Random().nextInt(100), 1, System.currentTimeMillis());
System.out.println(orderItem);
sourceContext.collect(orderItem);
TimeUnit.MILLISECONDS.sleep(1000);
}
} @Override
public void cancel() {
isRunning = false;
} public static class OrderItem {
public int id;
public int orderId;
public String shopName;
public double paidPrice;
public long updateTime; public OrderItem() {} public OrderItem(int id, int orderId, int shopId, String shopName, double paidPrice, int shopStatus, long updateTime) {
this.id = id;
this.orderId = orderId;
this.shopName = shopName;
this.paidPrice = paidPrice;
this.updateTime = updateTime;
} @Override
public String toString() {
return "OrderItem{" +
"id=" + id +
", orderId=" + orderId +
", shopName='" + shopName + '\'' +
", paidPrice=" + paidPrice +
", updateTime=" + updateTime +
'}';
}
}
}

二、核心代码

import com.aitogether.bigdata.onemetrics.runner.source.OrderItemSource;
import com.aitogether.bigdata.onemetrics.runner.source.OrderItemSource.OrderItem;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.util.OutputTag; import java.time.Duration; public class WindowWatermarkStrategy2 { public static void main(String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1); // 并行度 // 接入数据源
DataStream<OrderItem> orderItemStream = env.addSource(new OrderItemSource())
// 配置生成Watermark方式,设置最大乱序时间5秒,时间戳按updateTime获取(此时系统按EventTime计算Watermark)
.assignTimestampsAndWatermarks(WatermarkStrategy
.<OrderItem>forBoundedOutOfOrderness(Duration.ofSeconds(5)) // 最大乱序时间(maxOutOfOrderness)
.withTimestampAssigner((event, timestamp) -> event.updateTime)); // 获取时间戳方式,内部会自动跟踪已观察到的最大时间戳 // 按orderId分区,开启一个10秒的滚动时间窗口,并计算窗口内的最大ID
orderItemStream.keyBy(element -> element.orderId)
.window(TumblingEventTimeWindows.of(Time.seconds(10))) // 滚动【事件时间】窗口
.reduce((a, b) -> a.id > b.id ? a : b) // 求窗口内最大ID
.print("result"); env.execute(WindowWatermarkStrategy2.class.getSimpleName());
} }

三、结果分析

OrderItem{id=0, orderId=0, shopName='商品A', paidPrice=45.0, updateTime=1736238573687}
OrderItem{id=1, orderId=1, shopName='商品A', paidPrice=91.0, updateTime=1736238574692}
OrderItem{id=2, orderId=0, shopName='商品A', paidPrice=79.0, updateTime=1736238575696}
OrderItem{id=3, orderId=1, shopName='商品A', paidPrice=25.0, updateTime=1736238576699}
OrderItem{id=4, orderId=0, shopName='商品A', paidPrice=67.0, updateTime=1736238577701}
OrderItem{id=5, orderId=1, shopName='商品A', paidPrice=18.0, updateTime=1736238578706}
OrderItem{id=6, orderId=0, shopName='商品A', paidPrice=7.0, updateTime=1736238579710}
OrderItem{id=7, orderId=1, shopName='商品A', paidPrice=88.0, updateTime=1736238580714}
OrderItem{id=8, orderId=0, shopName='商品A', paidPrice=61.0, updateTime=1736238581719}
OrderItem{id=9, orderId=1, shopName='商品A', paidPrice=62.0, updateTime=1736238582724}
OrderItem{id=10, orderId=0, shopName='商品A', paidPrice=58.0, updateTime=1736238583726}
OrderItem{id=11, orderId=1, shopName='商品A', paidPrice=83.0, updateTime=1736238584729}
OrderItem{id=12, orderId=0, shopName='商品A', paidPrice=34.0, updateTime=1736238585732} // 12秒消息到达,此时Watermark:12-5=7
result> OrderItem{id=6, orderId=0, shopName='商品A', paidPrice=7.0, updateTime=1736238579710} // 触发7秒前的窗口计算,求出最大值6
result> OrderItem{id=5, orderId=1, shopName='商品A', paidPrice=18.0, updateTime=1736238578706} // 触发7秒前的窗口计算,求出最大值5 OrderItem{id=13, orderId=1, shopName='商品A', paidPrice=50.0, updateTime=1736238586738}
OrderItem{id=14, orderId=0, shopName='商品A', paidPrice=23.0, updateTime=1736238587743}
OrderItem{id=15, orderId=1, shopName='商品A', paidPrice=22.0, updateTime=1736238588747}
OrderItem{id=16, orderId=0, shopName='商品A', paidPrice=17.0, updateTime=1736238589752}
OrderItem{id=17, orderId=1, shopName='商品A', paidPrice=36.0, updateTime=1736238590758}
OrderItem{id=18, orderId=0, shopName='商品A', paidPrice=94.0, updateTime=1736238591763}
OrderItem{id=19, orderId=1, shopName='商品A', paidPrice=51.0, updateTime=1736238592768}
OrderItem{id=20, orderId=0, shopName='商品A', paidPrice=94.0, updateTime=1736238593773}
OrderItem{id=21, orderId=1, shopName='商品A', paidPrice=2.0, updateTime=1736238594778}
OrderItem{id=22, orderId=0, shopName='商品A', paidPrice=39.0, updateTime=1736238595782} // 22秒消息到达,此时Watermark:22-5=17
result> OrderItem{id=15, orderId=1, shopName='商品A', paidPrice=22.0, updateTime=1736238588747} // 触发17秒前的窗口计算,求出最大值15
result> OrderItem{id=16, orderId=0, shopName='商品A', paidPrice=17.0, updateTime=1736238589752} // 触发17秒前的窗口计算,求出最大值16 OrderItem{id=23, orderId=1, shopName='商品A', paidPrice=20.0, updateTime=1736238596784}
OrderItem{id=24, orderId=0, shopName='商品A', paidPrice=87.0, updateTime=1736238597790}
OrderItem{id=25, orderId=1, shopName='商品A', paidPrice=1.0, updateTime=1736238598794}
OrderItem{id=26, orderId=0, shopName='商品A', paidPrice=13.0, updateTime=1736238599797}
OrderItem{id=27, orderId=1, shopName='商品A', paidPrice=92.0, updateTime=1736238600801}
OrderItem{id=28, orderId=0, shopName='商品A', paidPrice=97.0, updateTime=1736238601804}
OrderItem{id=29, orderId=1, shopName='商品A', paidPrice=60.0, updateTime=1736238602806}
OrderItem{id=30, orderId=0, shopName='商品A', paidPrice=16.0, updateTime=1736238603811}
OrderItem{id=31, orderId=1, shopName='商品A', paidPrice=91.0, updateTime=1736238604813}
OrderItem{id=32, orderId=0, shopName='商品A', paidPrice=64.0, updateTime=1736238605817} // 后面数据以此类推
result> OrderItem{id=25, orderId=1, shopName='商品A', paidPrice=1.0, updateTime=1736238598794}
result> OrderItem{id=26, orderId=0, shopName='商品A', paidPrice=13.0, updateTime=1736238599797}
OrderItem{id=33, orderId=1, shopName='商品A', paidPrice=14.0, updateTime=1736238606821}
OrderItem{id=34, orderId=0, shopName='商品A', paidPrice=1.0, updateTime=1736238607825}

文档 https://apachecn.github.io/flink-doc-zh/#/docs/1.7-SNAPSHOT/README

Flink Watermark示例的更多相关文章

  1. [源码分析] 从源码入手看 Flink Watermark 之传播过程

    [源码分析] 从源码入手看 Flink Watermark 之传播过程 0x00 摘要 本文将通过源码分析,带领大家熟悉Flink Watermark 之传播过程,顺便也可以对Flink整体逻辑有一个 ...

  2. Flink - watermark生成

    参考,Flink - Generating Timestamps / Watermarks watermark,只有在有window的情况下才用到,所以在window operator前加上assig ...

  3. flink watermark介绍

    转发请注明原创地址 http://www.cnblogs.com/dongxiao-yang/p/7610412.html 一 概念 watermark是flink为了处理eventTime窗口计算提 ...

  4. Apache Flink 入门示例demo

    在本文中,我们将从零开始,教您如何构建第一个Apache Flink (以下简称Flink)应用程序. 开发环境准备 Flink 可以运行在 Linux, Max OS X, 或者是 Windows ...

  5. flink WaterMark之TumblingEventWindow

    1.WaterMark,翻译成水印或水位线,水印翻译更抽象,水位线翻译接地气. watermark是用于处理乱序事件的,通常用watermark机制结合window来实现. 流处理从事件产生,到流经s ...

  6. 老板让阿粉学习 flink 中的 Watermark,现在他出教程了

    1 前言 在时间 Time 那一篇中,介绍了三种时间概念 Event.Ingestin 和 Process, 其中还简单介绍了乱序 Event Time 事件和它的解决方案 Watermark 水位线 ...

  7. [白话解析] Flink的Watermark机制

    [白话解析] Flink的Watermark机制 0x00 摘要 对于Flink来说,Watermark是个很难绕过去的概念.本文将从整体的思路上来说,运用感性直觉的思考来帮大家梳理Watermark ...

  8. Flink - FlinkKafkaConsumer010

    Properties properties = new Properties(); properties.setProperty("bootstrap.servers", &quo ...

  9. Flink源码分析

    http://vinoyang.com/ http://wuchong.me Apache Flink源码解析之stream-source https://yq.aliyun.com/articles ...

  10. Flink Program Guide (5) -- 预定义的Timestamp Extractor / Watermark Emitter (DataStream API编程指导 -- For Java)

    本文翻译自Pre-defined Timestamp Extractors / Watermark Emitter ------------------------------------------ ...

随机推荐

  1. mybatis-plus.global-config.db-config.id-type=auto 和 @TableId(value = "id", type = IdType.ASSIGN_ID)哪个优先生效

    对于id自动生成的方式,有注解和配置两种. 含义相同:不过设置自动增长的时候必须保证数据库中id是自增,assign_id和assign_uuid则不需要. yml配置: mybatis-plus: ...

  2. 详细介绍Dubbo的SPI机制

    一.定义 Dubbo 的 SPI (Service Provider Interface) 机制是对 Java 原生 SPI 机制的增强和扩展,提供了更强大的扩展能力 二.Dubbo SPI 核心实现 ...

  3. DP刷题总结-2

    同步于Luogu blog T1 AT_joisc2007_buildi ビルの飾り付け (Building) 简化题意 最长上升子序列模板 分析 \(O(n^2)\)做法 考虑DP 定义状态:\(d ...

  4. FastJSON序列化扩展接口与特性详解

    结论先行 FastJSON 的 SerializeFilter 接口通过 动态拦截和修改序列化过程,可实现字段名重命名.敏感数据脱敏.字段过滤等高级功能.其核心子接口包括 PropertyPreFil ...

  5. 119K star!无需GPU轻松本地部署多款大模型,DeepSeek支持!这个开源神器绝了

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 "只需一行命令就能在本地运行Llama 3.DeepSeek-R1等前沿大模型,支 ...

  6. Linux还能拯救U盘???---U盘的起死回生

    作死;): 今天U盘在处理某些东西的时候,我性子急,直接把进程结束了,然后,就悲剧了( ̄ 'i  ̄;) 插到电脑上,一插就卡,一点就未响应,未响应.... PE系统打开还是老样子... 右键想格式化, ...

  7. 最新Typora1.9.5破解版下载与使用教程(Windows+Mac)

    一.Typora是什么? 一款 Markdown 编辑器和阅读器,能知道Typora的小伙伴,肯定也会用的 二.使用步骤 1.下载软件 夸克网盘:https://pan.quark.cn/s/2d6d ...

  8. 【SQL周周练】给你无酸纸、变色油墨,你能伪造多少美金?

    大家好,我是"蒋点数分",多年以来一直从事数据分析工作.从今天开始,与大家持续分享关于数据分析的学习内容. 本文是第 2 篇,也是[SQL 周周练]系列的第 2 篇.该系列是挑选或 ...

  9. Vue之“表单修饰符”

    1.lazy:失去焦点时处理 案例1 2.number:限制只能输入数字 案例1 3.trim:去掉前后空格 案例1

  10. Odoo14前端框架常用操作

    单页Web应用(single page web application,SPA): SPA 是一种特殊的 Web 应用,是加载单个 HTML 页面并在用户与应用程序交互时动态更新该页面的. 它将所有的 ...