之前一直用翻滚窗口,每条数据都只属于一个窗口,所有不需要考虑数据需要在多个窗口存的事情。

刚好有个需求,要用到滑动窗口,来翻翻 flink 在滑动窗口中,数据是怎么分配到多个窗口的

一段简单的测试代码:

val input = env.addSource(kafkaSource)
val stream = input
.map(node => {
Event(node.get("id").asText(), node.get("createTime").asText())
})
.windowAll(SlidingProcessingTimeWindows.of(Time.minutes(1), Time.seconds(10)))
.process(new ProcessAllWindowFunction[Event, Event, TimeWindow] {
override def process(context: Context, elements: Iterable[Event], out: Collector[Event]): Unit = {
val it = elements.iterator
var xx: Event = null
while (it.hasNext) {
xx = it.next()
}
out.collect(xx)
}
})
stream.print()

定义了一个长度为1分钟,滑动距离 10秒的窗口,所以正常每条数据应该对应 6 个窗口

在 process 中打个断点就可以追这段处理的源码了

数据的流向和  TumblingEventTimeWindows 是一样的,所以直接跳到对应数据分配的地方

WindowOperator.processElement,代码比较长,这里就精简一部分

@Override
public void processElement(StreamRecord<IN> element) throws Exception {
// 对应的需要分配的窗口
final Collection<W> elementWindows = windowAssigner.assignWindows(
element.getValue(), element.getTimestamp(), windowAssignerContext); //if element is handled by none of assigned elementWindows
boolean isSkippedElement = true; final K key = this.<K>getKeyedStateBackend().getCurrentKey(); if (windowAssigner instanceof MergingWindowAssigner) { } else {
// 循环遍历,将数据放到对应的窗口状态的 namesspace 中
for (W window: elementWindows) { // drop if the window is already late
if (isWindowLate(window)) {
continue;
}
isSkippedElement = false;
// 将数据放到对应的窗口中
windowState.setCurrentNamespace(window);
windowState.add(element.getValue()); registerCleanupTimer(window);
}
} }

for 循环就是将数据放到多个窗口的循环,看下 dubug 信息

看对应的6个窗口,从后往前的

窗口分配的代码,就对应这个方法的第一句:

final Collection<W> elementWindows = windowAssigner.assignWindows(
element.getValue(), element.getTimestamp(), windowAssignerContext);

assignWindows 的源码是根据 windowAssigner 的不同而改变的,这里是: SlidingProcessingTimeWindows,对应源码:

@Override
public Collection<TimeWindow> assignWindows(Object element, long timestamp, WindowAssignerContext context) {
timestamp = context.getCurrentProcessingTime();
List<TimeWindow> windows = new ArrayList<>((int) (size / slide));
long lastStart = TimeWindow.getWindowStartWithOffset(timestamp, offset, slide);
for (long start = lastStart;
start > timestamp - size;
start -= slide) {
windows.add(new TimeWindow(start, start + size));
}
return windows;
}

有个list 存储对应的窗口时间对象,list 的长度就是 窗口的长度 / 滑动的距离 (即一条数据会出现在几个窗口中)

这里用的是处理时间,所有Timestamp 直接从 处理时间中取,数据对应的 最后一个窗口的开始时间 lastStart 就用处理时间传到TimeWindow.getWindowStartWindOffset 中做计算

算出最后一个窗口的开始时间后,减 滑动的距离,就是上一个窗口的开始时间,直到 窗口的开始时间超出窗口的范围

对应的关键就是 lastStart 的计算,看源码:

/**
* Method to get the window start for a timestamp.
*
* @param timestamp epoch millisecond to get the window start.
* @param offset The offset which window start would be shifted by.
* @param windowSize The size of the generated windows.
* @return window start
*/
public static long getWindowStartWithOffset(long timestamp, long offset, long windowSize) {
return timestamp - (timestamp - offset + windowSize) % windowSize;
}

没指定 offset ,所以 offset 为0, lastStart =  timestamp - (timestamp - offset + windowSize) % windowSize

windowSize 是 滑动的距离,这里画了个图来说明计算的公式:

算出最后一个窗口的时间后,下面的 for 循环计算出数据对应的所有窗口,并创建一个时间窗口(这个时间窗口,并不是一个窗口,只是窗口的时间,表达一个窗口的开始时间和结束时间)

long lastStart = TimeWindow.getWindowStartWithOffset(timestamp, offset, slide);
for (long start = lastStart;
start > timestamp - size;
start -= slide) {
windows.add(new TimeWindow(start, start + size));
}

所以 17 对应的这条数据对应的窗口就有 (10-20), (15,25)

一条数据属于多少个窗口分配好了以后,就是把数据放到对应的窗口中了,flink 的窗口对应 state 的 namespace , 所以放到多个窗口,就是放到多个 namespace 中,对应的代码是:

windowState.setCurrentNamespace(window);
windowState.add(element.getValue());

选择 namespace,把数据放到对应的 state 中,后面窗口 fire 的时候,会从对应的 namespace 中 get 数据

欢迎关注Flink菜鸟公众号,会不定期更新Flink(开发技术)相关的推文

【源码解析】Flink 滑动窗口数据分配到多个窗口的更多相关文章

  1. [源码解析] Flink UDAF 背后做了什么

    [源码解析] Flink UDAF 背后做了什么 目录 [源码解析] Flink UDAF 背后做了什么 0x00 摘要 0x01 概念 1.1 概念 1.2 疑问 1.3 UDAF示例代码 0x02 ...

  2. [源码解析] Flink的groupBy和reduce究竟做了什么

    [源码解析] Flink的groupBy和reduce究竟做了什么 目录 [源码解析] Flink的groupBy和reduce究竟做了什么 0x00 摘要 0x01 问题和概括 1.1 问题 1.2 ...

  3. [源码解析] Flink的Slot究竟是什么?(1)

    [源码解析] Flink的Slot究竟是什么?(1) 目录 [源码解析] Flink的Slot究竟是什么?(1) 0x00 摘要 0x01 概述 & 问题 1.1 Fllink工作原理 1.2 ...

  4. [源码解析] Flink的Slot究竟是什么?(2)

    [源码解析] Flink 的slot究竟是什么?(2) 目录 [源码解析] Flink 的slot究竟是什么?(2) 0x00 摘要 0x01 前文回顾 0x02 注册/更新Slot 2.1 Task ...

  5. Flink 源码解析 —— Flink JobManager 有什么作用?

    JobManager 的作用 https://t.zsxq.com/2VRrbuf 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac ...

  6. Flink 源码解析 —— Flink TaskManager 有什么作用?

    TaskManager 有什么作用 https://t.zsxq.com/RZbu7yN 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- ...

  7. [源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampler

    [源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampler 目录 [源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampl ...

  8. [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader

    [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader 目录 [源码解析] PyTorch 分布式(2) --- 数据加载之DataLoader 0x00 摘要 0x01 ...

  9. Fresco源码解析 - DataSource怎样存储数据

    Fresco源码解析 - DataSource怎样存储数据 datasource是一个独立的 package,与FB导入的guava包都在同一个工程内 - fbcore. datasource的类关系 ...

随机推荐

  1. stm32f103c8t6 怎么使用IAP下载程序

    首先下载官方STM32F10X的IAP Bootloader源码,STM32F10x_AN2557_FW_V3.3.0. 用Keil4打开工程代码STM32F10x_AN2557_FW_V3.3.0P ...

  2. bug的编写技巧与级别划分

    一.bug编写技巧 确.清晰.简洁.完整.一致 二.bug包含的要素 缺陷ID.缺陷标题.测试环境.缺陷发现日期时间.缺陷提交人 缺陷优先级.缺陷严重等级.发现缺陷软件版本.测试类型 缺陷复现步骤.期 ...

  3. js 定时器 执行一次和重复执行

    1- 执行一次(延时定时器) var t1 = window.setTimeout(function() { console.log('1秒钟之后执行了') },1000) window.clearT ...

  4. Backpack V

    Description Given n items with size nums[i] which an integer array and all positive numbers. An inte ...

  5. jsp+ tinymce粘贴word

    最近公司做项目需要实现一个功能,在网页富文本编辑器中实现粘贴Word图文的功能. 我们在网站中使用的Web编辑器比较多,都是根据用户需求来选择的.目前还没有固定哪一个编辑器 有时候用的是UEditor ...

  6. LoadRunner学习目录

    已更新: 未更新: 1.loadrunner 11破解版及破解包 2.如何录制一个LR脚本 3.自定义loadrunner脚本

  7. Pytest权威教程25-配置

    目录 配置 命令行选项和配置文件设置 初始化:确定ROOTDIR和INIFILE 寻找rootdir 如何更改命令行选项默认值 内置的配置文件选项 返回: Pytest权威教程 配置 命令行选项和配置 ...

  8. Vue.js中 watch的理解以及深度监听

    如代码: <div> <p>FullName: {{fullName}}</p> <p>FirstName: <input type=" ...

  9. Java 从入门到进阶之路(十六)

    在之前的文章我们介绍了一下 Java 中类的多态,本章我们来看一下 Java 中类的内部类. 在 Java 中,内部类分为成员内部类和匿名内部类. 我们先来看一下成员内部类: 1.类中套类,外面的叫外 ...

  10. flink 读JDQ和写JDQ的流程

    ReadFromJDQ3 1)消费JDQ的必要信息,通过参数传入,有6个参数 2)获取flink JDQ3的鉴权客户端 3)根据鉴权客户端获取消费属性的配置 4)构建应用环境ENV和checkpoin ...