3. Flink 窗口

一. 基本概念

窗口是处理无限流的核心。窗口将流划分为固定大小的“桶”,方便程序员在上面应用各种计算。Window操作是流式数据处理的一种非常核心的抽象,它把一个无限流数据集分割成一个个有界的Window,然后就可以非常方便地定义作用于Window之上的各种计算操作。

二. 窗口分类

Window分为两类,窗口程序的通用结构详见下面,主要区别是针对Keyed Window,需要首先使用keyBy将stream转化为Keyed stream,然后使用window处理,但Non-Keyed Window直接使用windowAll处理。

1. Keyed Windows

  • 将输入原始流stream转换成多个Keyed stream
  • 每个Keyed stream会独立进行计算,这样多个Task可以对Windowing操作进行并行处理
  • 具有相同Key的数据元素会被发到同一个Task中进行处理
stream
       .keyBy(...)               <-  keyed versus non-keyed windows
       .window(...)              <-  required: "assigner"
      [.trigger(...)]            <-  optional: "trigger" (else default trigger)
      [.evictor(...)]            <-  optional: "evictor" (else no evictor)
      [.allowedLateness(...)]    <-  optional: "lateness" (else zero)
      [.sideOutputLateData(...)] <-  optional: "output tag" (else no side output for late data)
       .reduce/aggregate/apply()      <-  required: "function"
      [.getSideOutput(...)]      <-  optional: "output tag"

2. Non-Keyed Windows

  • 原始流stream不会被分割成多个逻辑流
  • 所有的Windowing操作逻辑只能在一个Task中进行处理,计算并行度为1
stream
       .windowAll(...)           <-  required: "assigner"
      [.trigger(...)]            <-  optional: "trigger" (else default trigger)
      [.evictor(...)]            <-  optional: "evictor" (else no evictor)
      [.allowedLateness(...)]    <-  optional: "lateness" (else zero)
      [.sideOutputLateData(...)] <-  optional: "output tag" (else no side output for late data)
       .reduce/aggregate/apply()      <-  required: "function"
      [.getSideOutput(...)]      <-  optional: "output tag"
三. 窗口分配器

窗口按照驱动类型可以分成时间窗口和计数窗口,而按照具体的分配规则,又有滚动窗口、滑动窗口、会话窗口、全局窗口四种。除去需要自定义的全局窗口外,其他常用的类型Flink中都给出了内置的分配器实现,我们可以方便地调用实现各种需求。
1.滚动窗口
在这里插入图片描述

滚动窗口赋值器将每个元素赋给一个固定长度大小的窗口。滚动窗口有一个固定的大小且相互不重叠
特点:

  • 时间对齐 : 默认是aligned with epoch(整点、整分、整秒等),可以通过offset参数改变对齐方式。
  • 窗口长度固定
  • 每条数据只属于一个窗口,无重叠

代码示例:

stream.window(TumblingEventTimeWindows.of(Time.seconds(5)))
stream.window(TumblingProcessingTimeWindows.of(Time.seconds(5)))

2.滑动窗口

在这里插入图片描述

滑动窗口赋值器将每个元素赋值给一个或多个固定长度大小的窗口
特点:

  • 时间对齐 : 默认是aligned with epoch(整点、整分、整秒等),可以通过offset参数改变对齐方式。
  • 窗口长度固定
  • 数据存在重叠

代码示例:

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

3.会话窗口
在这里插入图片描述

与滚动窗口和滑动窗口相比,会话窗口不重叠且没有固定的开始和结束时间。当会话窗口在一段时间内没有接收到元素时,即发生不活动间隙【session gap】时,会话窗口将关闭。
特点

  • 时间无对齐
  • event不重叠
  • 没有固定开始和结束时间

代码示例:

stream.window(EventTimeSessionWindows.withGap(Time.minutes(10)))

4.全局窗口
在这里插入图片描述

全局窗口赋值器将具有相同键的所有元素分配给同一个全局窗口。此窗口模式仅在您指定自定义触发器时才有用。否则,将不执行任何计算,因为全局窗口没有一个可以处理聚合元素的自然结束。

代码示例:

stream.window(GlobalWindows.create())

5.计数窗口

计数窗口很好理解,当窗口的数据量达到了设定的窗口大小时,窗口函数就会被触发

代码示例:

stream.countWindowAll(10);
四. 窗口函数

窗口函数定义了要对窗口中收集的数据做的计算操作,根据处理的方式可以分为两类:增量聚合函数和全窗口函数。

1. 增量聚合函数(ReduceFunction / AggregateFunction)
增量聚合函数就是窗口中的数据每来一个就在之前的结果上聚合一次,典型的增量聚合函数有两个:ReduceFunction和AggregateFunction。

### Flink 自定义触发器(Trigger)实现 在 Apache Flink 中,`Trigger` 是用于控制何时计算窗口的结果的核心组件之一。默认情况下,Flink 提供了一些内置的 `Trigger` 实现,但如果这些无法满足需求,则可以通过扩展 `Trigger<T, W>` 类来自定义触发逻辑。 以下是通过 `StreamExecutionEnvironment` 和 `TimeWindow` 的自定义触发器示例: #### 自定义 Trigger 示例代码 ```java import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.api.java.tuple.Tuple2; 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.streaming.api.windowing.triggers.Trigger; import org.apache.flink.streaming.api.windowing.triggers.TriggerResult; import org.apache.flink.streaming.api.windowing.windows.TimeWindow; public class CustomTriggerExample { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); DataStream<Tuple2<String, Integer>> stream = env.fromElements( Tuple2.of("key1", 1), Tuple2.of("key1", 2), Tuple2.of("key2", 3), Tuple2.of("key2", 4)) .assignTimestampsAndWatermarks(WatermarkStrategy.<Tuple2<String, Integer>>forMonotonousTimestamps() .withTimestampAssigner((event, timestamp) -> System.currentTimeMillis())); DataStream<Integer> result = stream.keyBy(value -> value.f0) .window(TumblingEventTimeWindows.of(Time.seconds(5))) .trigger(new MyCustomTrigger<>()) // 使用自定义触发器 .sum(1); result.print(); env.execute("Custom Trigger Example"); } /** * 定义一个简单的自定义触发器。 */ public static class MyCustomTrigger<T, W extends TimeWindow> extends Trigger<T, W> { @Override public TriggerResult onElement(T element, long timestamp, W window, TriggerContext ctx) throws Exception { // 当接收到任意元素时立即触发评估 return TriggerResult.FIRE_AND_PURGE; } @Override public TriggerResult onProcessingTime(long time, W window, TriggerContext ctx) throws Exception { // 不基于处理时间触发任何操作 return TriggerResult.CONTINUE; } @Override public TriggerResult onEventTime(long time, W window, TriggerContext ctx) throws Exception { // 如果事件时间到达窗口结束时间则触发 if (time >= window.getEnd()) { return TriggerResult.FIRE; } return TriggerResult.CONTINUE; } @Override public void clear(W window, TriggerContext ctx) throws Exception { // 清理状态的操作 } } } ``` #### 关键点解析 - **自定义触发器类**:继承 `Trigger<T, W>` 并重写其核心方法来定义触发行为[^6]。 - **onElement 方法**:每当新数据进入窗口时调用此方法。在此处可以根据条件决定是否触发窗口计算并清除数据[^7]。 - **onProcessingTime/onEventTime 方法**:分别针对处理时间和事件时间设置额外的触发条件[^8]。 - **clear 方法**:清理与特定窗口关联的状态,在某些场景下可能需要显式释放资源[^9]。 以上程序展示了如何创建一个简单但功能强大的自定义触发器,并将其应用于滚动事件时间窗口中。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值