Flink时间窗口程序骨架结构

轻装上阵Flink--在IDEA上开发基于Flink的实时数据流程序 前言 本文介绍如何在IDEA上快速开发基于Flink框架的DataStream程序。先直接上手! 环境清单 案例是在win7运行。安装VirtualBox,在VirtualBox上安装Centos操作系统。所有资源都在百度云上,有需要请直接下载。安装教程基本都是傻瓜式,文章不做讲述,有需要直接网上搜索。 资源 版本 VirtualBox 5.2.16... 阅读详情

前言

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>() {
                    @Override
                    public void run(SourceContext<Long> sourceContext) throws Exception {
                        while (true) {
                            Threads.sleep(500);
                            sourceContext.collect(ThreadLocalRandom.current().nextLong(100));
                        }
                    }

                    @Override
                    public void cancel() {

                    }
                }).keyBy(i -> "all")
                // 窗口分配器
                .window(new WindowAssigner<Long, TimeWindow>() {
                    static final long WINDOW_SIZE = 10_000L;

                    @Override
                    public 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));
                    }

                    @Override
                    public Trigger<Long, TimeWindow> getDefaultTrigger(StreamExecutionEnvironment streamExecutionEnvironment) {
                        // 默认触发器,废弃了
                        return null;
                    }

                    @Override
                    public TypeSerializer<TimeWindow> getWindowSerializer(ExecutionConfig executionConfig) {
                        // 窗口序列化器
                        return new TimeWindow.Serializer();
                    }

                    @Override
                    public boolean isEventTime() {
                        // 是否基于事件时间语义
                        return false;
                    }
                })
                // 窗口触发器
                .trigger(new Trigger<Long, TimeWindow>() {
                    private long max_register_processing_time = 0L;

                    @Override
                    public 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;
                    }

                    @Override
                    public TriggerResult onProcessingTime(long l, TimeWindow timeWindow, TriggerContext triggerContext) throws Exception {
                        // 窗口计算并清除数据
                        return TriggerResult.FIRE_AND_PURGE;
                    }

                    @Override
                    public TriggerResult onEventTime(long l, TimeWindow timeWindow, TriggerContext triggerContext) throws Exception {
                        return null;
                    }

                    @Override
                    public void clear(TimeWindow timeWindow, TriggerContext triggerContext) throws Exception {
                        triggerContext.deleteProcessingTimeTimer(timeWindow.maxTimestamp());
                    }
                })
                // 窗口移除器
                .evictor(new Evictor<Long, TimeWindow>() {
                    @Override
                    public 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);
                            }
                        }
                    }

                    @Override
                    public void evictAfter(Iterable<TimestampedValue<Long>> iterable, int i, TimeWindow timeWindow, EvictorContext evictorContext) {
                        // 窗口计算后触发
                    }
                })
                // 因为是基于事件时间语义,不存在迟到数据,所以无须设置 allowedLateness、sideOutputLateData
                // 窗口处理函数
                .process(new ProcessWindowFunction<Long, String, String, TimeWindow>() {
                    @Override
                    public 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的window分类(滚动、滑动、会话、全局)、窗口函数讲透 那么窗口的起止时间将变为[0:15:00.000~1:14:59.999),这样你将得到起始时间在0:15:00,1:15:00,2:15:00的窗口。例如,在没有偏移的情况下,时间窗口会做一个对齐,那么1小时窗口的起止时间可以是[0:00:00.000~0:59:59.999)。ProcessWindowFunction是全量计算函数,如果需要依赖窗口中的所有数据或需要获取窗口中的状态数据和窗口元数据(窗口开始时间、窗口结束时间等),就需要使用ProcessWindowFunction。 阅读详情

相关推荐

告别Hello World:用Flink 1.17.2和IDEA从零写一个能处理真实文本的WordCount程序

本文详细介绍了如何使用Flink 1.17.2和IDEA从零构建一个能处理真实文本的WordCount程序,涵盖环境配置、项目初始化、数据源处理、生产级实现及性能优化。通过实战案例,帮助开发者掌握流处理核心思想,提升实时数据处理能力。

weixin_30839881的博客 433

Flink 程序结构 第二篇(共两篇)

这次接着上次的第一篇继续分享:分区 key 的指定、输出结果和程序触发 (4) 分区 key 的指定 Flink 的某些转换算子,如 join、coGroup、groupBy 算子,需要先将 DataStream 或 DataSet 数据集转换成对应的 KeyedStream 或 GroupedDataSet,主要目的是将相同的 key 值的数据路由到相同的 pipeline 中,然后进行下一步的...

Brave_heart4pzj的博客 3590

大数据技术之Flink

第1章 Flink概述1.1 Flink是什么1.2 Flink特点表 Flink 和 Streaming对比计算模型 流计算 微批处理时间语义 事件时间、处理时间 处理时间窗口 多、灵活 少、不灵活(窗口必须是批次的整数倍)状态 有 没有流式SQL 有 没有1.4 Flink的应用场景1.5 Flink分层API第2章 Flink快速上手2.1 创建项目在准备好所有的开发环境之后,我们就可以开始开发自己的第一个Flink程序了。

weixin_42158569的博客 1150

Flink学习02 - Flink运行架构

Flink学习02 - Flink运行架构学习资料Flink运行架构一、Flink运行时的组件作业管理器(JobManager)任务管理器(TaskManager)资源管理器(ResourceManager)分发器(Dispatcher)二、任务提交流程三、任务调度原理思考:并行度(Parallelism)TaskManger 与 Slots并行子任务的分配程序与数据流(DataFlow)执行图(ExecutionGraph)数据传输形式任务链(Operator Chains) 学习资料 尚硅谷2021最新

SeafyLiang的博客 331

(二)Flink 程序运行结构

本章我们将通过入门案例,从开发环境准备到程序运行输出,带领大家初步认识 Flink。

springk的专栏 366

避坑指南:STM32H7不同DMA控制器(MDMA/DMA2D/BDMA)到底该用哪个?

本文深入解析STM32H7系列芯片的DMA控制器选型策略,涵盖MDMA、DMA2D、BDMA等不同控制器的架构原理与性能对比。通过实际项目案例,提供高速TCM数据搬运、GUI加速、低功耗传输等场景的选型决策树,并分享性能优化与调试技巧,帮助工程师避开常见陷阱,提升系统效率。

weixin_26876073的博客 447

[Flink02] Flink架构和原理

这是继第一节之后的Flink入门系列的第二篇,本篇主要内容是是:了解Flink运行模式、Flink调度原理、Flink分区、Flink安装。

渡江客涂鸦板 1665

Windows打印体系结构之Windows内置打印驱动程序

时间是一种很残酷的东西,它只会冲淡能够冲淡的,但也会洗尽铅华帮你留下该留下的。1.3.4、Windows内置打印驱动程序现在的Windows下的打印机驱动程序通常分为V4打印机驱动程序(V4 Printer Driver)、XPS打印机驱动程序(XPSDrv)、微软通用打印机驱动程序(Microsoft UniversalPrinter Driver)、微软PostScri...

weixin_34309435的博客 2253

详解 Flink 的 ProcessFunction API

相较于 map、filter 和 window 等特定的具体的操作而言,Flink 在底层 API 中提炼出一个统一通用的 process 操作,它是所有转换算子的一个概括性的表达,可以在对应的接口中自定义处理逻辑,而这一层接口就被叫作“处理函数”(ProcessFunction)

weixin_44480009的博客 1678

Intel AX系列网卡5G热点开启失败?Linux下3种修复方案实测(含Ubuntu/OpenWRT)

本文针对Intel AX系列网卡在Linux系统下无法开启5G热点的常见问题,深入剖析了其根源在于内核LAR机制与驱动的兼容性。文章提供了三种实测有效的修复方案:针对内核低于5.5的模块参数法、适用于高内核版本的hostapd补丁编译法以及高阶的驱动修改法,帮助用户在Ubuntu、OpenWRT等环境中成功启用5G热点功能。

weixin_29291863的博客 729

2025使用VM虚拟机安装配置Macos苹果系统下Flutter开发环境保姆级教程--下篇

我们可以看到,目前配置基本完成了,现在有两个错,一个是Chrome没安装,这个自己安装就可以了,如果不需要WEB测试也不影响开发,另一个是Simulator还没安装,这个主要是安装的时间太长了,也就是上面的这一步。这里要注意,这个下载安装会比较慢,有的载会特别卡造成安装失败,最好的办法是挂一个VPN,保证安装完成,不过这个可以在以后单独进行安装。至此,基本的安装完毕,好多人会以为Xcode就OK了,其实还没有,我们还缺少命令行工具包,这个在安装完Flutter以后进行设置。

weixin_48803304的博客 1411

Flink窗口理论到实践 | 大数据技术

⭐⭐个人主页🔎哆啦A梦撩代码🎉欢迎关注🔎👍⭐️留言📝

小知识吖 1万+

故障诊断 | 小波变换机械设备外圈振动信号故障诊断

故障诊断 | 小波变换机械设备外圈振动信号故障诊断

天天酷科研的博客 506

Flink(十)窗口计算

一、简介 窗口计算:flink的灵魂 窗口计算就是把无界数据流切分为有限大小的“bucket”—>窗口(bucket/window/panel),在窗口上应用计算换上完成计算处理 核心:窗口的划分和计算 我们经常需要在一个时间窗口维度上对数据进行聚合,窗口是流处理应用中经常需要解决的问题。Flink的窗口算子为我们提供了方便易用的API,我们可以将数据流切分成一个个窗口,对窗口内的数据进行处理。 大致程序结构: 按照有没有进行keyby分成了两种 不同的处理方式 首先,我们要决定是否对一个DataS

又小雨的博客 2840

matlab 点云按高程进行赋色【2024最新版】

首先按照Z轴方向求取所在点云的高程极值(包括高程最大值与最小值),然后将高程归一化到0~255范围内,最后采用matlab中的colormap颜色渲染函数进行赋色并保存渲染结果到点云。

点云侠的博客 3340

【RK3399 Ubuntu】基于rk3399的lxde的lubuntu桌面移植适配记录之第二步:最基本Ubuntu根文件系统的相关常用工具完善

文章目录1.使能系统网络管理服务1.1 安装驱动modules管理工具1.2 使能网络服务2.终端自动补全命令功能3.安装ssh服务4.安装系统信息查看工具5.安装resize工具6.安装lrzsz工具7.使能rc.local服务7.1 修改服务Install块7.2 使能rc.local.service服务7.3 添加执行脚本且赋予执行权限 续接上文,本文主要完善前面已经制作好的rootfs 1.使能系统网络管理服务 1.1 安装驱动modules管理工具 apt-get install kmod 1.

wang 恒 的博客 1384

flink(2)datastream API基本使用

在本文中,我们从头开始,通过在flink上运行一个流分析程序来学习如何使用flink。 Wikipedia提供了一个IRC频道,这个频道会记录所有在维基百科上编辑的内容。我们通过flink来读取这个频道,并计算每个用户在一个时间窗口内编辑的字节数。这个很简单,在flink中实现只需要几分钟。但这个可以给我们一个了解flink并编写更复杂计算程序的台阶。 开启一个maven工程 我们通过flink提...

陆多多的专栏 399

Oracle EBS Interface/API(29) - AP发票API

续上编文章《Oracle EBS Interface/API(28) - 客制化开发AP付款API》背景,介绍如调用官方提供标准AP发票API调用 用户界面 Invoices(Payables Manager->Invoices->Entry->Invoices) Open Interface Invoices(Payables Manager->Invoices->Entry->Open Interface Invoices) 快速参考 参考点 内容.

陈显平的专栏-Oracle EBS 1148

Flink开发-时间语义与窗口分类

Flink开发-时间语义与窗口分类1.Flink的三种时间语义1.1 Event Time 事件时间1.2 Processing Time 处理时间1.3 Ingestion Time 进入时间1.4 设置时间标准2.Window Assinger2.1 滚动窗口(Tumbling Windows)2.2 滑动窗口(Sliding Windows)2.3 会话窗口(Session Windows)2.4 GlobalWindow3.窗口程序的骨架3.1 Non-Keyed Windows3.2 Keyed

学习记录 592

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Flink DataStream 编程入门

流处理是 Flink 的核心,流处理的数据集用 DataStream 表示。数据流从可以从各种各样的数据源中创建(消息队列、Socket 和 文件等),经过 DataStream 的各种 transform 操作,最终输出文件或者标准输出。这个过程跟之前文章中介绍的 Flink 程序基本骨架一样。本篇介绍 DataStream 相关的入门知识。 Flink 101 为了学习 Flink 的朋友...

渡码的专栏 523

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

flink java

这里写目录标题5. Flink流处理API5.1 Environment执行环境5.2 Source数据源env.fromCollection 从集合读取数据env.readTextFile 从文件读取数据从kafka读取数据自定义数据源5.3 Transform转换算子一 单数据流基本转换:mapflatMapfilter二 基于key的分组转换keyBy()指定key的三种方式聚合(Aggregation)算子reduce归并三 多数据流转换ConnectUnion合流和Connect的区别?四 数据重

蛙爸爸的博客 4202

如何用AI快速构建Flink流处理应用

减少样板代码:自动生成80%的基础配置代码避免常见坑点:内置了生产环境最佳实践交互式迭代:可以边调试边优化对于想快速入门Flink的开发者,强烈推荐试试这种开发方式。不用从零开始啃文档,直接通过自然语言描述就能获得可运行的项目基础,大幅降低了大数据开发的门槛。

AmberLeopard26的博客 393

flink窗口计算

有了事件时间,基于窗口的聚合(事件时间列上的一种特殊的分组和聚合),每个时间窗口是一个组,每一行数据可以属于多个窗口。的字符串不会触发窗口计算,同时,清除器会清理30秒之前的数据,比如,某一次输入后,到了30秒之后继续输出,会重新开始统计。因此,如果滑动窗口的步长小于窗口大小,则滑动窗口可以重叠。:滚动窗口分配器将每个元素分配给指定大小的窗口,滚动窗口具有固定的大小,并且不重叠。事件被窗口分配器分配到窗口后,接下来需要指定想要在每个窗口上执行的计算函数(即窗口函数),以便对窗口内的数据进行处理。

z1987865446的博客 1158

flink window理解

使用window的大致骨架: // Keyed Window stream .keyBy(...) <- 按照一个Key进行分组 .window(assigner) <- 将数据流中的元素分配到相应的窗口中 [.trigger(...)] <- 指定触发器Trigger(可选) [.evictor(...)] <- 指定清除器E

epitomizelu的专栏 230

Flink零基础实战教程:股票价格数据流实时处理

之前的文章《10行Flink WordCount程序背后的万字深度解析,读懂Flink原理和架构》使用WordCount展示了Flink程序的基本结构,本文将以股票价格来演示如何使用Flink的DataStream API。通过本文,你可以学到: 定义相关数据结构。 Flink流处理程序的骨架。 Flink的执行环境概念。 自定义Source、设置时间戳和Watermark。 数据结构 Fli...

皮皮鲁的AI星球 2912

Flink源码拾遗<三>:DataStream

我们在上一篇文章中通过一个wordcount例子引入了对StreamExecutionEnviornment的思考,这一篇文章我们依然从这个例子说起,我们来看看wordcount中算子转化的部分吧。 DataStream<WordWithCount> windowCount = text.flatMap(new FlatMapFunction<String, WordWithCount>() { public void flatMap(String val

Li_DeSheng的博客 517
上一篇: Flink事件时间和Watermark
下一篇: Flink窗口分配器WindowAssigner
长河0
长河0 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄11年 1267粉丝 375原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

长河0

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值