Flink时间窗口程序骨架结构
前言
Flink 作业的基本骨架结构包含三部分:创建执行环境、定义数据处理逻辑、提交并执行Flink作业。
日常大部分 Flink 作业是基于时间窗口计算模型的,同样的,开发一个Flink时间窗口作业也有一套基本的骨架结构,了解这套结构有助于我们更快地上手时间窗口作业开发。
窗口程序的基本骨架
一个Flink时间窗口作业的代码基本骨架如下所示:
stream.keyBy(...) <- 仅 keyed 窗口需要.window(...) <- 必填项:"assigner"[.trigger(...)] <- 可选项:"trigger" (省略则使用默认 trigger)[.evictor(...)] <- 可选项:"evictor" (省略则不使用 evictor)[.allowedLateness(...)] <- 可选项:"lateness" (省略则为 0)[.sideOutputLateData(...)] <- 可选项:"output tag" (省略则不对迟到数据使用 side output).reduce/aggregate/apply() <- 必填项:"function"[.getSideOutput(...)] <- 可选项:"output tag"
时间窗口作业对数据逻辑的处理,主要包含以下步骤:
- 对数据流进行分组,将DataStream装换为KeyedStream
- 指定窗口分配器 WindowAssigner,将数据划分到对应的窗口
- 指定窗口触发器 Trigger,决定了窗口何时关闭并计算
- 指定窗口移除器 Evictor,它可以在窗口计算前后对窗口内的数据进行移除
- allowedLateness 允许迟到的数据,事件时间语义下,即使事件时钟到达窗口关闭时间,窗口仍会保留一段时间以等待迟到的数据
- sideOutputLateData 针对窗口关闭后到达的迟到数据,可以将其输出到另外一条数据流,对计算结果做修正
- ProcessFunction 窗口内数据的处理函数
时间窗口作业实战
了解了时间窗口作业的基本骨架,以及相关组件的作用,接下来就实战一把。
如下示例程序,数据源每秒会生成2个一百以内的随机数,然后数据经过 keyBy 算子分组,这里为了简单,数据全部划分为一组,KeySelector 统一返回 “all”。
分组后,窗口分配器将数据划分到对应的窗口。这里基于处理时间语义,统一分配10秒大小的时间窗口,时间窗口被Flink封装为 TimeWindow 对象,包含两个属性,分别是起始时间戳和结束时间戳。
一旦有数据进入窗口,Trigger#onElement 就会触发,返回值决定了Flink如何处理窗口。显然我们的逻辑是时间到达窗口的结束时间,窗口就会触发计算并关闭,所以我们会注册一个 ProcessingTime 事件,窗口结束时间一到,Trigger#onProcessingTime 就会触发,窗口就会开始计算。
窗口计算前,还需要经过移除器Evictor。它有两个方法,分别在窗口计算前和计算后调用,在这里你可以根据条件移除窗口内无须计算的数据。示例程序中,把小于10的数移除掉了。
最终,窗口内的数据会交给 ProcessWindowFunction 处理,窗口内的数据被Flink封装成迭代器Iterable,通过它可以获得所有窗口内的数据。示例程序 中,我们所有元素打印出来并求和。
public class TimeWindowStructure {public static void main(String[] args) throws Exception {StreamExecutionEnvironment environment = StreamExecutionEnvironment.getExecutionEnvironment();environment.addSource(new SourceFunction<Long>() {@Overridepublic void run(SourceContext<Long> sourceContext) throws Exception {while (true) {Threads.sleep(500);sourceContext.collect(ThreadLocalRandom.current().nextLong(100));}}@Overridepublic void cancel() {}}).keyBy(i -> "all")// 窗口分配器.window(new WindowAssigner<Long, TimeWindow>() {static final long WINDOW_SIZE = 10_000L;@Overridepublic Collection<TimeWindow> assignWindows(Long event, long timestamp, WindowAssignerContext windowAssignerContext) {// 把数据分配到对应的窗口,一条数据甚至可以分配到多个窗口// 这里根据处理时间 分配10秒大小的窗口final long processingTime = windowAssignerContext.getCurrentProcessingTime();long start = processingTime / WINDOW_SIZE * WINDOW_SIZE;long end = start + WINDOW_SIZE;return List.of(new TimeWindow(start, end));}@Overridepublic Trigger<Long, TimeWindow> getDefaultTrigger(StreamExecutionEnvironment streamExecutionEnvironment) {// 默认触发器,废弃了return null;}@Overridepublic TypeSerializer<TimeWindow> getWindowSerializer(ExecutionConfig executionConfig) {// 窗口序列化器return new TimeWindow.Serializer();}@Overridepublic boolean isEventTime() {// 是否基于事件时间语义return false;}})// 窗口触发器.trigger(new Trigger<Long, TimeWindow>() {private long max_register_processing_time = 0L;@Overridepublic TriggerResult onElement(Long element, long timestamp, TimeWindow timeWindow, TriggerContext triggerContext) throws Exception {// 每个元素进入窗口,都会触发该方法 返回结果决定了窗口是否计算或关闭// 我们是根据处理时间窗口结束时间来判断是否触发的,所以注册一个处理时间事件即可if (timeWindow.maxTimestamp() > max_register_processing_time) {max_register_processing_time = timeWindow.maxTimestamp();triggerContext.registerProcessingTimeTimer(max_register_processing_time);}return TriggerResult.CONTINUE;}@Overridepublic TriggerResult onProcessingTime(long l, TimeWindow timeWindow, TriggerContext triggerContext) throws Exception {// 窗口计算并清除数据return TriggerResult.FIRE_AND_PURGE;}@Overridepublic TriggerResult onEventTime(long l, TimeWindow timeWindow, TriggerContext triggerContext) throws Exception {return null;}@Overridepublic void clear(TimeWindow timeWindow, TriggerContext triggerContext) throws Exception {triggerContext.deleteProcessingTimeTimer(timeWindow.maxTimestamp());}})// 窗口移除器.evictor(new Evictor<Long, TimeWindow>() {@Overridepublic void evictBefore(Iterable<TimestampedValue<Long>> iterable, int i, TimeWindow timeWindow, EvictorContext evictorContext) {// 窗口计算前触发Iterator<TimestampedValue<Long>> iterator = iterable.iterator();while (iterator.hasNext()) {TimestampedValue<Long> next = iterator.next();Long value = next.getValue();if (value < 10) {iterator.remove();System.err.println("Evicted:" + value);}}}@Overridepublic void evictAfter(Iterable<TimestampedValue<Long>> iterable, int i, TimeWindow timeWindow, EvictorContext evictorContext) {// 窗口计算后触发}})// 因为是基于事件时间语义,不存在迟到数据,所以无须设置 allowedLateness、sideOutputLateData// 窗口处理函数.process(new ProcessWindowFunction<Long, String, String, TimeWindow>() {@Overridepublic void process(String group, ProcessWindowFunction<Long, String, String, TimeWindow>.Context context, Iterable<Long> iterable, Collector<String> collector) throws Exception {TimeWindow window = context.window();StringBuilder builder = new StringBuilder();builder.append("[" + window.getStart() + "-" + window.maxTimestamp() + "] elements:");Iterator<Long> iterator = iterable.iterator();Long sum = 0L;while (iterator.hasNext()) {Long value = iterator.next();sum += value;builder.append(value + " ");}builder.append(", sum:" + sum);System.err.println(builder.toString());}});environment.execute();}
}
运行Flink作业,控制台输出:
Evicted:3
Evicted:6
Evicted:1
[1722665800000-1722665809999] elements:89 17 16 57 94 47 67 98 , sum:485
Evicted:6
Evicted:4
[1722665810000-1722665819999] elements:86 50 71 95 36 10 55 43 96 36 28 87 89 50 53 35 63 95 , sum:1078
Evicted:4
Evicted:8
Evicted:0
[1722665820000-1722665829999] elements:85 20 42 86 46 20 32 45 91 59 57 64 31 67 78 71 28 , sum:922
尾巴
了解Flink时间窗口作业的基本骨架结构,理清Flink时间窗口的数据流转过程,有助于我们更快上手Flink时间窗口作业的开发。
Flink时间窗口作业包含的核心组件有:WindowAssigner、Window、Trigger、Evictor、ProcessWindowFunction。
相关文章:
Flink时间窗口程序骨架结构
前言 Flink 作业的基本骨架结构包含三部分:创建执行环境、定义数据处理逻辑、提交并执行Flink作业。 日常大部分 Flink 作业是基于时间窗口计算模型的,同样的,开发一个Flink时间窗口作业也有一套基本的骨架结构,了解这套结构有助…...
计算机视觉之可做什么
1、计算机视觉的应用 计算机视觉在我们生活中已经有了很广泛的应用,在我们可见、不可见;可感知、不可感知的地方,深深地影响了我们的生活、生产方式。 日常生活:美颜相机、火车站刷脸进站、线上办理业务的身份认证、自动驾驶等等…...
观察者模式的思考
观察者模式由来 观察者模式(Observer Pattern)是一种行为型设计模式,它的起源可以追溯到20世纪90年代初,由设计模式四人帮(Erich Gamma, Richard Helm, Ralph Johnson 和 John Vlissides)在其著作《设计模…...
ORACLE SELECT INTO 赋值为空,抛出 NO DATA FOUND 异常
例子: DECLARE ORDER_NUM VARCHAR2(20); BEGIN SELECT S.ORDER_NUM INTO ORDER_NUM FROM SALES_ORDER S WHERE S.ID122344; DBMS_OUTPUT.PUT_LINE(单号: || ORDER_NUM); END; 在查询结果为空的情况下,以上代码会报错:未找到任何数据 解决方…...
GPT提示词
参考 提示词大全: GPT提示词大全(中英文双语)持续更新 提示词.com...
Redis协议详解及其异步应用
目录 一、Redis Pipeline(管道)概述优点使用场景工作原理Pipeline 的基本操作步骤C 示例(使用 [hiredis](https://github.com/redis/hiredis) 库) 二、Redis 事务概述事务的前提事务特征(ACID 分析)WATCH 命…...
LeetCode213:打家劫舍II
题目链接:213. 打家劫舍 II - 力扣(LeetCode) 代码如下 class Solution { public:int rob(vector<int>& nums) {if(nums.size() 0) return 0;if(nums.size() 1) return nums[0];if(nums.size() 2) return max(nums[0…...
linux一二三章那些是重点呢
第一章 静态库动态库的区别 什么是库 库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接 拿来用的变量、函数或类。 如何制作 静态动态库 静态库: GCC 进行链接时,会把静态库中代码打…...
C语言中的程序入口:超越main函数的探索
在C语言中,尽管main函数是标准程序的默认入口点,但借助编译器特性和链接器选项,我们可以指定其他函数作为程序的入口。GCC编译器通过-e选项,允许我们将任何符合签名的函数作为程序的入口。这一特性可以用于特定的实验需求、特定系…...
《面试之MQ篇》
《面试之MQ篇》 1. 为什么要使用MQ 首先,面试官问的第一个问题或者说是逼问的一个问题:“为什么要使用MQ”其实面试官问这个问题就是想考察你MQ的特性,这个时候呢,我们必须要答出三点:解耦、异步、削峰。 1. 解耦 1. 传统系统…...
Git 分支操作-开发规范
一、背景 在实际开发中,一般在主分支的基础上单独创建一个新的分支进行开发,最后合并到master分支,而不是直接在master分支进行开发。 二、新建分支 1、初始状态,local为本地分支,remote为远程分支 2、单击 “Remot…...
JSONArray根据指定字段去重
JSONArray dataList new JSONArray();这儿省略dataList 加数据的过程 dataList new JSONArray(dataList.stream().distinct().collect(Collectors.toList())); Set<String> timestamps new HashSet<>();根据时间字段去重 dataList dataList.stream().map(obj -…...
线程有哪几种状态? 分别说明从一种状态到另一种状态转变有哪些方式?
在 Java 中,线程的生命周期管理通过不同的状态来跟踪。一个线程在其生命周期中可以处于多种状态,不同的状态之间会通过特定的事件发生转变。以下是 Java 线程的几种状态及其之间的转移方式: 1. 线程的状态 1.1 NEW(新建状态&…...
自注意力机制self-attention中的KV 缓存
在自注意力机制中,KV 缓存(Key-Value Caching)主要用于加速模型在推理阶段的计算,尤其是在处理长序列或者生成任务(如文本生成)时,这种缓存机制可以显著提高效率。 1. KV 缓存的背景 在 Trans…...
前端库--nanoid(轻量级的uuid)
文章目录 定义:生成方式:现实使用:NanoID 只有 108 个字节那么大NanoID更安全NanoID它既快速又紧凑 使用步骤1.安装nanoid包2.引入使用3.使用4.自定义字母 定义: UUID 是 通用唯一识别码(Universally Unique Identifierÿ…...
计算机基础-什么是网络端口?
网络端口可以想象成一个大型公寓楼的邮箱。每个公寓楼(这里指的是一个计算机或服务器)有很多个邮箱(即网络端口),每个邮箱都有一个独一无二的编号(端口号)。当一封信(网络数据包&…...
力扣动态规划基础版(斐波那契类型)
70. 爬楼梯https://leetcode.cn/problems/climbing-stairs/ 70.爬楼梯 方法一 动态规划 考虑转移方程和边界条件: f(x) f(x -1) f(x - 2);f(1) 1;f&…...
Java重修笔记 InetAddress 类和 Socket 类
InetAddress 类相关方法 1. 获取本机 InetAddress 对象:getLocalHost public static InetAddress getLocalHost() throws UnknownHostException 返回值:本地主机的名字和地址 异常:UnknownHostException - 如果本地主机名无法解析成地址 2…...
秋招突击——8/6——万得数据面试总结
文章目录 引言正文面经整理一1、讲一下java的多态,重载,重写的概念,区别2、说一下Java的数组,链表的结构,优缺点3、创建java线程的方式有哪些,具体说说4、创建线程池呢、每个参数的意义5、通过那几种方式保…...
STM32定时器
目录 STM32定时器概述 STM32基本定时器 基本定时器的功能 STM32基本定时器的寄存器 STM32通用定时器 STM32定时器HAL库函数 STM32定时器概述 从本质上讲定时器就是“数字电路”课程中学过的计数器(Counter),它像“闹钟”一样忠实地为处…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
