Flink窗口(2)—— Window API

目录

窗口分配器

时间窗口

计数窗口

全局窗口

窗口函数

增量聚合函数

全窗口函数(full window functions)

增量聚合和全窗口函数的结合使用

Window API 主要由两部分构成:窗口分配器(Window Assigners)和窗口函数(Window Functions)

stream.keyBy(<key selector>).window(<window assigner>) //指明窗口的类型.aggregate(<window function>) //定义窗口具体的处理逻辑

在window()方法中传入一个窗口分配器;

在aggregate()方法中传入一个窗口函数;

窗口分配器

指定窗口的类型,定义数据应该被“分配”到哪个窗口

方法:.window()

参数:WindowAssigner

返回值:WindowedStream

如果是非按键分区窗口,那么直接调用.windowAll()方法,同样传入一个WindowAssigner,返回的是 AllWindowedStream

时间窗口

滚动处理时间窗口

stream.keyBy(...)  
//1..of()方法需要传入一个 Time 类型的参数 size,表示滚动窗口的大小
.window(TumblingProcessingTimeWindows.of(Time.seconds(5))) //窗口大小
//2.通过设置偏移量offset 来调整起始点的时间戳
.window(TumblingProcessingTimeWindows.of(Time.days(1), Time.hours(-8))) //窗口大小,偏移量
.aggregate(...)

默认的窗口起始点时间戳是窗口大小的整倍数

如果我们定义 1 天的窗口,默认就从 0 点开始;如果定义 1 小时的窗口,默认就从整点开始

如果不想用默认值,就需要设置好偏移量

偏移量的作用:标准时间戳其实就是1970 年 1 月 1 日 0 时 0 分 0 秒 0 毫秒开始计算的一个毫秒数,而这个时间是以 UTC 时间,也就是 0 时区(伦敦时间)为标准的。我们所在的时区是东八区,也就是 UTC+8,跟 UTC 有 8小时的时差。我们定义 1 天滚动窗口时,如果用默认的起始点,那么得到就是伦敦时间每天 0点开启窗口,这时是北京时间早上 8 点。那怎样得到北京时间每天 0 点开启的滚动窗口呢?只要设置-8 小时的偏移量就可以了

滑动处理时间窗口

stream.keyBy(...)
//窗口大小,滑动步长(同样也可以设置偏移量)
.window(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5)))
.aggregate(...)

处理时间会话窗口

stream.keyBy(...)
//超时时间
.window(ProcessingTimeSessionWindows.withGap(Time.seconds(10)))
.aggregate(...)

以上是静态设置了超时时间,也可以动态设置:

.window(ProcessingTimeSessionWindows.withDynamicGap(newSessionWindowTimeGapExtractor<Tuple2<String, Long>>() {@Overridepublic long extract(Tuple2<String, Long> element) { 
// 提取 session gap 值返回, 单位毫秒
//提取了数据元素的第一个字段,用它的长度乘以 1000 作为会话超时的间隔return element.f0.length() * 1000;}
}

滚动事件时间窗口

stream.keyBy(...)
.window(TumblingEventTimeWindows.of(Time.seconds(5))) 
.aggregate(...)

滑动事件时间窗口

stream.keyBy(...)
.window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5)))
.aggregate(...)

事件时间会话窗口

stream.keyBy(...)
.window(EventTimeSessionWindows.withGap(Time.seconds(10)))
.aggregate(...)

处理时间和事件时间的逻辑完全相同

计数窗口

滚动计数窗口:.countWindow(10) //窗口大小

滑动计数窗口:.countWindow(10,3) //窗口大小,滑动步长

每个窗口统计 10 个数据,每隔 3 个数据就统计输出一次结果

全局窗口

.window(GlobalWindows.create());

需要自定义触发器

窗口函数

WindowedStream——>DataStream

增量聚合函数

像 DataStream 的简单聚合一样,每来一条数据就立即进行计算,中间只要保持一个简单的聚合状态就可以

区别在于不立即输出结果,而是要等到窗口结束时间

归约函数(ReduceFunction):和简单聚合时使用的ReduceFunction完全一样


聚合函数(AggregateFunction):取消类型一致的限制,直接基于 WindowedStream 调 用.aggregate()方法,不需要经过map处理;这个方法需要传入一个AggregateFunction 的实现类作为参数,源码如下:

@PublicEvolving
public interface AggregateFunction<IN, ACC, OUT> extends Function, Serializable {/*** Creates a new accumulator, starting a new aggregate.** <p>The new accumulator is typically meaningless unless a value is added via {@link* #add(Object, Object)}.** <p>The accumulator is the state of a running aggregation. When a program has multiple* aggregates in progress (such as per key and window), the state (per key and window) is the* size of the accumulator.** @return A new accumulator, corresponding to an empty aggregate.*/ACC createAccumulator();/*** Adds the given input value to the given accumulator, returning the new accumulator value.** <p>For efficiency, the input accumulator may be modified and returned.** @param value The value to add* @param accumulator The accumulator to add the value to* @return The accumulator with the updated state*/ACC add(IN value, ACC accumulator);/*** Gets the result of the aggregation from the accumulator.** @param accumulator The accumulator of the aggregation* @return The final aggregation result.*/OUT getResult(ACC accumulator);/*** Merges two accumulators, returning an accumulator with the merged state.** <p>This function may reuse any of the given accumulators as the target for the merge and* return that. The assumption is that the given accumulators will not be used any more after* having been passed to this function.** @param a An accumulator to merge* @param b Another accumulator to merge* @return The accumulator with the merged state*/ACC merge(ACC a, ACC b);
}

IN:输入数据类型

ACC:累加器类型

OUT:输出数据类型

AggregateFunction 接口中有四个方法:

除了继承AggregateFunction,自定义聚合函数之外,Flink为我们提供了一系列预定义的简单聚合方法,如sum()/max()/maxBy()/min()/minBy(),可以直接基于WindowedStream调用

全窗口函数(full window functions)

全窗口函数需要先收集窗口中的数据,并在内部缓存起来,等到窗口要输出结果的时候再取出数据进行计算

典型的批处理方式,适用于一些基于全部数据才能进行的运算等等

窗口函数(WindowFunction)

stream.keyBy(<key selector>).window(<window assigner>)//基于 WindowedStream 调用.apply()方法,传入一个 WindowFunction 的实现类.apply(new MyWindowFunction());

WindowFunction的实现类中可以获取到包含窗口所有数据的可迭代集合(Iterable),还可以拿到窗口本身的信息

功能可以被 ProcessWindowFunction(处理窗口函数,见下) 全覆盖


处理窗口函数(ProcessWindowFunction)

增强版的 WindowFunction

基于 WindowedStream 调用.process()方法,传入一个 ProcessWindowFunction 的实现类

ProcessWindowFunction的泛型:ProcessWindowFunction<IN,OUT,KEY,W>

分别是输入数据类型,输出数据类型,分区键的类型,Window类型(比如,是时间窗口,就是TimeWindow)

process()方法的定义:

示例代码如下,自定义窗口处理函数来处理数据:

public class UvCountByWindowExample {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();env.setParallelism(1);SingleOutputStreamOperator<Event> stream = env.addSource(new ClickSource()).assignTimestampsAndWatermarks(WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ZERO).withTimestampAssigner(new SerializableTimestampAssigner<Event>() {@Overridepublic long extractTimestamp(Event element, long recordTimestamp) {return element.timestamp;}}));// 将数据全部发往同一分区,按窗口统计UVstream.keyBy(data -> true).window(TumblingEventTimeWindows.of(Time.seconds(10))).process(new UvCountByWindow()).print();env.execute();}//自定义窗口处理函数public static class UvCountByWindow extends ProcessWindowFunction<Event, String, Boolean, TimeWindow>{@Overridepublic void process(Boolean aBoolean, Context context, Iterable<Event> elements, Collector<String> out) throws Exception {HashSet<String> userSet = new HashSet<>();// 遍历所有数据,放到Set里去重for (Event event: elements){userSet.add(event.user);}// 结合窗口信息,包装输出内容Long start = context.window().getStart();Long end = context.window().getEnd();out.collect("窗口: " + new Timestamp(start) + " ~ " + new Timestamp(end)+ " 的独立访客数量是:" + userSet.size());}}}

这里的Event是一个POJO类,ClickSource是自定义的数据源,其代码如下:
Event.java:

public class Event {public String user;public String url;public Long timestamp;public Event() {}public Event(String user, String url, Long timestamp) {this.user = user;this.url = url;this.timestamp = timestamp;}@Overridepublic String toString() {return "Event{" +"user='" + user + '\'' +", url='" + url + '\'' +", timestamp=" + new Timestamp(timestamp) +'}';}
}

ClickSource.java: 

public class ClickSource implements SourceFunction<Event> {// 声明一个布尔变量,作为控制数据生成的标识位private Boolean running = true;@Overridepublic void run(SourceContext<Event> ctx) throws Exception {Random random = new Random();    // 在指定的数据集中随机选取数据String[] users = {"Mary", "Alice", "Bob", "Cary"};String[] urls = {"./home", "./cart", "./fav", "./prod?id=1", "./prod?id=2"};while (running) {ctx.collect(new Event(users[random.nextInt(users.length)],urls[random.nextInt(urls.length)],Calendar.getInstance().getTimeInMillis()));// 隔1秒生成一个点击事件,方便观测Thread.sleep(1000);}}@Overridepublic void cancel() {running = false;}}

增量聚合和全窗口函数的结合使用

增量聚合函数处理计算会更高效;而全窗口函数的优势在于提供了更多的信息

我们之前在调用 WindowedStream 的.reduce()和.aggregate()方法时,只是简单地直接传入了一个 ReduceFunction 或 AggregateFunction 进行增量聚合。除此之外,其实还可以传入第二个参数:一个全窗口函数,可以是 WindowFunction 或者 ProcessWindowFunction

处理机制:

基于第一个参数(增量聚合函数)来处理窗口数据,每来一个数据就做一次聚合;等到窗口需要触发计算时,则调用第二个参数(全窗口函数)的处理逻辑输出结果。需要注意的是,这里的全窗口函数就不再缓存所有数据了,而是直接将增量聚合函数的结果拿来当作了 Iterable 类型的输入。一般情况下,这时的可迭代集合中就只有一个元素了

学习课程链接:【尚硅谷】Flink1.13实战教程(涵盖所有flink-Java知识点)_哔哩哔哩_bilibili 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/410896.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Error]连接iPhone调试时提示Failed to prepare the device for development.

环境&#xff1a; iPhone 7 Plus iOS 15.8 Xcode14.2 问题&#xff1a; 连接iPhone设备运行时&#xff0c;设备旁提示如下文案。 Failed to prepare the device for development. 这时强行点击运行按钮&#xff0c;会弹窗提示如下文案。 The run destination ZDMiPhone is n…

Java实现海南旅游景点推荐系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统&#xff…

pytorch学习(一)线性模型

文章目录 线性模型 pytorch是一个基础的python的科学计算库&#xff0c;它有以下特点&#xff1a; 类似于numpy&#xff0c;但是它可以使用GPU可以用它来定义深度学习模型&#xff0c;可以灵活的进行深度学习模型的训练和使用 线性模型 线性模型的基本形式为&#xff1a; f ( x…

开发实践6_project

要求&#xff1a; ① 页面写入超链接&#xff0c;获取所有数据item&#xff0c;显示在另一个页面&#xff0c;1min内&#xff0c;即使数据有变化&#xff0c;页面内容不变&#xff0c;1min后点击超链接可获取最新信息&#xff1b; ② 使用middleware完成用户请求路径判断 &am…

机器学习之常用激活函数

人工神经网络中最基本的单元叫神经元,又叫感知器,它是模拟人脑神经系统的神经元(分析和记忆)、树突(感知)、轴突(传导)的工作原理,借助计算机的快速计算和存储来实现。它的主体结构如下: 激活函数常用类型有:线性激活函数、符号激活函数、Sigmoid激活函数、tanh激活…

rabbitmq基础教程(ui,java,springamqp)

概述&#xff1a;安装看我上篇文章Docker安装rabbitmq-CSDN博客 任务一 创建一个队列 这样创建两个队列 在amq.fanout交换机里面发送数据 模拟发送数据 发送消息&#xff0c;发现一下信息&#xff1a; 所以得出理论&#xff0c;消息发送是先到交换机&#xff0c;然后由交换机…

云边协同的 RTC 如何助力即构全球实时互动业务实践

作者&#xff1a;即构科技 由 51 CTO 主办的“WOT 全球技术创新大会 2023深圳站”于 11 月 24 日 - 25 日召开&#xff0c;即构科技后台技术总监肖潇以“边缘容器在全球音视频场景的探索与实践”为主题进行分享。 边缘计算作为中心云计算的补充&#xff0c;通过边缘容器架构和…

【SpringCloud】之Sentinel--服务容错的应用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringCloud开发之Sentinel--服务容错的应用》。…

muduo网络库剖析——监听者EpollPoller类

muduo网络库剖析——监听者EpollPoller类 前情从muduo到my_muduo 概要epoll原理解析epoll提供的接口epoll的触发模式epoll实现多路复用 框架与细节成员函数使用方法 源码结尾 前情 从muduo到my_muduo 作为一个宏大的、功能健全的muduo库&#xff0c;考虑的肯定是众多情况是否…

【论文阅读】One For All: Toward Training One Graph Model for All Classification Tasks

目录 0、基本信息1、研究动机2、创新点——One For All &#xff1a;unique features3、准备4、具体实现4.1、用TAGs统一来自不同领域的图数据4.2、用NOI&#xff08;NODES-OF-INTEREST&#xff09;统一不同图任务4.2.1、NOI子图4.2.2、NOI提示结点 4.3、用于图的上下文学习&am…

【算法小记】深度学习——循环神经网络相关原理与RNN、LSTM算法的使用

文中程序以Tensorflow-2.6.0为例 部分概念包含笔者个人理解&#xff0c;如有遗漏或错误&#xff0c;欢迎评论或私信指正。 卷积神经网络在图像领域取得了良好的效果&#xff0c;卷积核凭借优秀的特征提取能力通过深层的卷积操作可是实现对矩形张量的复杂计算处理。但是生活中除…

C++、QT 数字合成游戏

一、项目介绍 数字合成游戏 基本要求&#xff1a; 1&#xff09;要求游戏界面简洁美观&#xff0c;且符合扫雷的游戏风格。 2&#xff09;需要有游戏操作或者规则说明&#xff0c;方便玩家上手。 3&#xff09;需具有开始游戏&#xff0c;暂停游戏&#xff0c;结束游戏等方便玩…