目前有许多数据分析的场景从批处理到流处理的演变, 虽然能够将批处理做为流处理的特殊状况来处理,可是分析无穷集的流数据一般须要思惟方式的转变而且具备其本身的术语(例如,“windowing(窗口化)”、“at-least-once(至少一次)”、“exactly-once(只有一次)” )。html
对于刚刚接触流处理的人来讲,这种转变和新术语可能会很是混乱。 Apache Flink 是一个为生产环境而生的流处理器,具备易于使用的 API,能够用于定义高级流分析程序。apache
Flink 的 API 在数据流上具备很是灵活的窗口定义,使其在其余开源流处理框架中脱颖而出。windows
在这篇文章中,咱们将讨论用于流处理的窗口的概念,介绍 Flink 的内置窗口,并解释它对自定义窗口语义的支持。session
下面咱们结合一个现实的例子来讲明。框架
就拿交通传感器的示例:统计通过某红绿灯的汽车数量之和?函数
假设在一个红绿灯处,咱们每隔 15 秒统计一次经过此红绿灯的汽车数量,以下图:学习
能够把汽车的通过当作一个流,无穷的流,不断有汽车通过此红绿灯,所以没法统计总共的汽车数量。可是,咱们能够换一种思路,每隔 15 秒,咱们都将与上一次的结果进行 sum 操做(滑动聚合),以下:spa
这个结果彷佛仍是没法回答咱们的问题,根本缘由在于流是无界的,咱们不能限制流,但能够在有一个有界的范围内处理无界的流数据。.net
所以,咱们须要换一个问题的提法:每分钟通过某红绿灯的汽车数量之和?
这个问题,就至关于一个定义了一个 Window(窗口),window 的界限是1分钟,且每分钟内的数据互不干扰,所以也能够称为翻滚(不重合)窗口,以下图:htm
第一分钟的数量为8,第二分钟是22,第三分钟是27。。。这样,1个小时内会有60个window。
再考虑一种状况,每30秒统计一次过去1分钟的汽车数量之和:
此时,window 出现了重合。这样,1个小时内会有120个 window。
扩展一下,咱们能够在某个地区,收集每个红绿灯处汽车通过的数量,而后每一个红绿灯处都作一次基于1分钟的window统计,即并行处理:
一般来说,Window 就是用来对一个无限的流设置一个有限的集合,在有界的数据集上进行操做的一种机制。window 又能够分为基于时间(Time-based)的 window 以及基于数量(Count-based)的 window。
Flink DataStream API 提供了 Time 和 Count 的 window,同时增长了基于 Session 的 window。同时,因为某些特殊的须要,DataStream API 也提供了定制化的 window 操做,供用户自定义 window。
下面,主要介绍 Time-Based window 以及 Count-Based window,以及自定义的 window 操做,Session-Based Window 操做将会在后续的文章中讲到。
正如命名那样,Time Windows 根据时间来聚合流数据。例如:一分钟的 tumbling time window 收集一分钟的元素,并在一分钟事后对窗口中的全部元素应用于一个函数。
在 Flink 中定义 tumbling time windows(翻滚时间窗口) 和 sliding time windows(滑动时间窗口) 很是简单:
tumbling time windows(翻滚时间窗口)
输入一个时间参数
1 2 3 |
data.keyBy(1) .timeWindow(Time.minutes(1)) //tumbling time window 每分钟统计一次数量和 .sum(1); |
sliding time windows(滑动时间窗口)
输入两个时间参数
1 2 3 |
data.keyBy(1) .timeWindow(Time.minutes(1), Time.seconds(30)) //sliding time window 每隔 30s 统计过去一分钟的数量和 .sum(1); |
有一点咱们尚未讨论,即“收集一分钟的元素”的确切含义,它能够归结为一个问题,“流处理器如何解释时间?”
Apache Flink 具备三个不一样的时间概念,即 processing time, event time 和 ingestion time。
这里能够参考我下一篇文章:
《从0到1学习Flink》—— 介绍Flink中的Event Time、Processing Time和Ingestion Time
Apache Flink 还提供计数窗口功能。若是计数窗口设置的为 100 ,那么将会在窗口中收集 100 个事件,并在添加第 100 个元素时计算窗口的值。
在 Flink 的 DataStream API 中,tumbling count window 和 sliding count window 的定义以下:
tumbling count window
输入一个时间参数
1 2 3 |
data.keyBy(1) .countWindow(100) //统计每 100 个元素的数量之和 .sum(1); |
sliding count window
输入两个时间参数
1 2 3 |
data.keyBy(1) .countWindow(100, 10) //每 10 个元素统计过去 100 个元素的数量之和 .sum(1); |
Flink 的内置 time window 和 count window 已经覆盖了大多数应用场景,可是有时候也须要定制窗口逻辑,此时 Flink 的内置的 window 没法解决这些问题。为了还支持自定义 window 实现不一样的逻辑,DataStream API 为其窗口机制提供了接口。
下图描述了 Flink 的窗口机制,并介绍了所涉及的组件:
到达窗口操做符的元素被传递给 WindowAssigner。WindowAssigner 将元素分配给一个或多个窗口,可能会建立新的窗口。
窗口自己只是元素列表的标识符,它可能提供一些可选的元信息,例如 TimeWindow 中的开始和结束时间。注意,元素能够被添加到多个窗口,这也意味着一个元素能够同时在多个窗口存在。
每一个窗口都拥有一个 Trigger(触发器),该 Trigger(触发器) 决定什么时候计算和清除窗口。当先前注册的计时器超时时,将为插入窗口的每一个元素调用触发器。在每一个事件上,触发器均可以决定触发(即、清除(删除窗口并丢弃其内容),或者启动并清除窗口。一个窗口能够被求值屡次,而且在被清除以前一直存在。注意,在清除窗口以前,窗口将一直消耗内存。
当 Trigger(触发器) 触发时,能够将窗口元素列表提供给可选的 Evictor,Evictor 能够遍历窗口元素列表,并能够决定从列表的开头删除首先进入窗口的一些元素。而后其他的元素被赋给一个计算函数,若是没有定义 Evictor,触发器直接将全部窗口元素交给计算函数。
计算函数接收 Evictor 过滤后的窗口元素,并计算窗口的一个或多个元素的结果。 DataStream API 接受不一样类型的计算函数,包括预约义的聚合函数,如 sum(),min(),max(),以及 ReduceFunction,FoldFunction 或 WindowFunction。
这些是构成 Flink 窗口机制的组件。 接下来咱们逐步演示如何使用 DataStream API 实现自定义窗口逻辑。 咱们从 DataStream [IN] 类型的流开始,并使用 key 选择器函数对其分组,该函数将 key 相同类型的数据分组在一块。
1 2 |
SingleOutputStreamOperator<xxx> data = env.addSource(...); data.keyBy() |
负责将元素分配到不一样的 window。
Window API 提供了自定义的 WindowAssigner 接口,咱们能够实现 WindowAssigner 的
1 |
public abstract Collection<W> assignWindows(T element, long timestamp) |
方法。同时,对于基于 Count 的 window 而言,默认采用了 GlobalWindow 的 window assigner,例如:
1 |
keyBy.window(GlobalWindows.create()) |
Trigger 即触发器,定义什么时候或什么状况下移除 window
咱们能够指定触发器来覆盖 WindowAssigner 提供的默认触发器。 请注意,指定的触发器不会添加其余触发条件,但会替换当前触发器。
驱逐者,即保留上一 window 留下的某些元素
利用 Flink 的内部窗口机制和 DataStream API 能够实现自定义的窗口逻辑,例如 session window。
对于现代流处理器来讲,支持连续数据流上的各类类型的窗口是必不可少的。 Apache Flink 是一个具备强大功能集的流处理器,包括一个很是灵活的机制,能够在连续数据流上构建窗口。 Flink 为常见场景提供内置的窗口运算符,以及容许用户自定义窗口逻辑。
一、https://flink.apache.org/news/2015/12/04/Introducing-windows.html