窗口(Window)

本文翻译自文档Windows

-----------------------------------

Flink使用窗口的概念,根据element的时间戳或者其他指标,将可能无限的DataStream分割为有限的数据切片(slice)。我们在处理无限数据流以及进行聚合element的transformation时需要此种窗口分割。

注意:我们在此文档中讨论的大多是keyed windowing,即window是应用在KeyedStream上的。关键字下的窗口具有一定的优势,即它可以在element传递给user function之前就能按照window和关键字共同二次分割element。由于不同关键字的element可以相互独立处理,所以该工作可以在cluster之上分布式进行。有关non-keyed window的信息,请查看文档non-keyed windowing

一、基础部分

一个带窗口的transformation至少需要定义一个key(见文档specifying keys)、一个window assigner以及一个window functionkey将无限而无关键字的流分割成逻辑的有关键字数据流,而window assigner将element赋值给有限的各自关键字的窗口(per-key window)。最后window function会用于处理每个窗口的element。

带窗口transformation的基础结构如下所示:

DataStream<T> input = ...;

input.keyBy(<key selector>)
  .window(<window
assigner>)
  .<windowed transformation>(<window
function>);

我们会在接下来的一节中单独讲window assigners

Window
transformation可以是reduce()fold()或者apply()之一,它们相对应的需要一个ReduceFunctionFoldFunctionWindowFunction。我们将会在下文window functions中具体描述定义一个带窗口transformation的不同方式。

在更进一步的用例中,你可以定义一个Trigger来决定一个窗口什么时候才是ready for
processing
的。相关详细内容见于本文triggers小节。

二、Window
Assigners

Window
assigner定义了数据流的element将如何分割进有限的数据切片。Flink自带预先实现了针对多数典型用例的window assigner,以tumbling
window
sliding
window
session
window
global
window
命名,此外,你还可以通过继承WindowAssigner类来自定义自己的window
assigner。除了global
window,所有自带window
assigner都是基于时间(可以是processing
time或者event time)来分配element。有关Flink如何处理时间,请见文档event time

在描述这些window
assigner如何用于Flink程序之前,我们先描述它们的工作机制。我们将使用抽象图来可视化每个assigner的工作机制:在下面的内容中,紫色圈是数据流的element,它们以不同的关键字进行分割(在该例中关键字为user1,
user2, user3),x轴表示时间的进展。

2.1Global Windows

Global
Window的定义表明我们不会进一步将element二次分割到窗口中。每个element将被分配到一个单独的各自关键字的窗口中。该窗口化模式仅仅在同时拥有一个自定义trigger时才有用。否则由于global
window没有用以聚合element的常态结束,所以不会发生任何计算。

2.2 Tumbling Window

Tumbling
window

assigner将element分配到一个固定长度、无重叠的窗口,且该窗口的window
size有用户定义。例如,如果你定义window
size为5分钟,window
function每次调用都会得到5分钟的element。

2.3 Sliding Windows

Sliding
window

assigner和tumbling
window一样,将element分配到一个固定长度(等于window
size),但该窗口可重叠,重叠的大小由用户定义的参数window
slide定义。由于窗口可重叠,故一个element可以分配到多个窗口中去。

例如,你可以定义一个window
size为10分钟,且slide为5分钟。在该窗口中,每此window
function会得到10分钟的element,且5分钟调用一次。

2.4 Session Windows

Session window assigner在窗口边界需要根据到达数据调整的情况下十分适用。Tumbling
windows和sliding
windows的assigner都将element分配到开始于固定时间点并且拥有固定window
size的窗口中。而在session中,你可以让关键字窗口开始于它们自己的时间点,并且在一段无活动情况(inactivity)出现时结束窗口。该窗口的配置参数session
gap
定义了等待新数据多长时间就结束一个session。

2.5 定义一个Window
Assigner

除了GlobalWindows,内置window
assigner都有两个版本,一个处理processing-time
windowing,另一个处理event-time
windowing。Processing-time
assigner根据worker设备的当前时钟来分配element,而event-time
assigner根据element的时间戳来分配窗口。有关processing
time和event time的区别以及如何给element分配时间戳的内容,请见文档event time

下面的代码片段展示了在程序中如何使用每个window assigner:

DataStream<T>
input = ...;

//
tumbling event-time windows

input
  .keyBy(<key
selector>)
  .window(TumblingEventTimeWindows.of(Time.seconds(5)))
  .<windowed transformation>(<window
function>);

//
sliding event-time windows

input
  .keyBy(<key
selector>)
  .window(SlidingEventTimeWindows.of(Time.seconds(10),
Time.seconds(5)))
  .<windowed transformation>(<window
function>);

//
event-time session windows

input
  .keyBy(<key
selector>)
  .window(EventTimeSessionWindows.withGap(Time.minutes(10)))
  .<windowed transformation>(<window
function>);

//
tumbling processing-time windows

input
  .keyBy(<key
selector>)
  .window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
  .<windowed transformation>(<window
function>);

//
sliding processing-time windows

input
  .keyBy(<key
selector>)
  .window(SlidingProcessingTimeWindows.of(Time.seconds(10),
Time.seconds(5)))
  .<windowed transformation>(<window
function>);

//
processing-time session windows

input
  .keyBy(<key
selector>)
  .window(ProcessingTimeSessionWindows.withGap(Time.minutes(10)))
  .<windowed transformation>(<window
function>);

//
global windows

input
  .keyBy(<key
selector>)
  .window(GlobalWindows.create())
  .<windowed transformation>(<window
function>);

注意:我们可以通过Time.millisecond(x)Time.second(x)Time.minutes(x)等等方法来定义时间。

三、Window
Functions

在系统确定一个窗口已经做好了处理的准备时(有关系统是如何确定窗口可处理,见文档trigger),Flink将使用Window
Function处理每个窗口中的element。

Window
Function可以是ReduceFunctionFlodFunctionWindowFunction。由于Flink在element到达各自窗口时递增地聚合它们,所以前两个方法可以更加高效地执行。WindowFunction获取窗口中所有element的Iterable以及这些element所属的窗口的额外元信息(meta
information)。

由于Flink在调用使用了WindowFunction的窗口化transformation之前,必须在内部缓存一个窗口中所有element,所以此种transformation无法像其他情况一样高效执行。我们可以将WindowFunction和一个ReduceFunctionFoldFunction相结合,从而在递增聚合窗口中element的同时,还可以获取WindowFunction接收的额外信息,通过这种方式,我们可以缓解上述问题。我们在下面会对各种情况一一举例。

3.1 ReduceFunction

一个reduce方法定义了两个值如何结合形成一个element。Flink可以使用它来递增地聚合窗口中的element。

程序中的ReduceFunction如下例:

DataStream<Tuple2<String,
Long>> input = ...;

input.keyBy(<key
selector>)
  .window(<window
assigner>)
  .reduce(new
ReduceFunction<Tuple2<String, Long>> {
    public Tuple2<String,
Long> reduce(Tuple2<String,
Long> v1, Tuple2<String, Long> v2)
{

      return new Tuple2<>(v1.f0,
v1.f1 + v2.f1);
    }
  });

一个ReduceFunction定义了两个输入中的element是如何结合产生一个输出element的。在上面的例子中,将会计算出一个窗口中所有element的第二个域的总和。

3.2 FoldFunction

一个fold方法可以定义如下:

DataStream<Tuple2<String,
Long>> input = ...;

input
  .keyBy(<key
selector>)
  .window(<window
assigner>)
  .fold("",
new
FoldFunction<Tuple2<String, Long>,
String>> {
    public String fold(String
acc, Tuple2<String, Long> value) {
      return acc + value.f1;
    }
  });

一个FoldFunction定义了输入中的element如何加上一个累加初始值(在本例中为"",即空字符串)。在上例中,将会计算出输入中所有Long域的字符串连接(concatenation)结果。

3.3 WindowFunction - 一般情况

WindowFunction以性能开销的增加,换来了最大的灵活性(它可以获得key和Window的引用)。WindowFunction由于无法递增聚合窗口中的element,在窗口准备好处理之前,Flink都必须内部缓存整个窗口,所以带来性能上的开销。一个WindowFunction将得到处理的窗口中所有element的Iterable。WindowFunction接口的签名如下所示:

public
interface
WindowFunction<IN,
OUT, KEY, W extends Window> extends Function,
Serializable {

  /**
  * Evaluates the window
and outputs none or several elements.

  *
  * @param key The key
for which this window is evaluated.

  * @param window The
window that is being evaluated.

  * @param input The
elements in the window being evaluated.

  * @param out A
collector for emitting elements.

  *
  * @throws Exception The
function may throw exceptions to fail the program and trigger recovery.

  */
  void apply(KEY
key, W window, Iterable<IN> input,
Collector<OUT> out) throws Exception;
}

下面我们举例使用WindowFunction来计算一个窗口中element数量。我们之所以选择WindowFunction,是因为我们想在发送计数的同时,还想访问并一同发送有关窗口的信息。这将非常低效,我们应当在练习中和一个ReduceFunction一同实现WindowFunction。在下一节中,我们将会看到将ReduceFunctionWindowFunction结合来获取递增地聚合以及之前添加的WindowFunction的信息

DataStream<Tuple2<String,
Long>> input = ...;

input
  .keyBy(<key
selector>)
  .window(<window
assigner>)
  .apply(new
MyWindowFunction());

/*
... */

public
class
MyWindowFunction implements
WindowFunction<Tuple<String, Long>, String,
String, TimeWindow> {

void
apply(String
key, TimeWindow window, Iterable<Tuple<String,
Long>> input, Collector<String> out)
{

  long
count = 0;
  for (Tuple<String,
Long> in: input) {
    count++;
  }
  out.collect("Window:
" + window + "count:
" + count);
}

}

3.4 带有递增聚合的WindowFunction

一个WindowFunction可以与ReduceFunction或者FoldFunction结合,在结合后,ReduceFunction/FoldFunction将会用来在窗口的element到达时递增聚合它们,而WindowFunction则会在窗口准备好处理时得到已经聚合后的结果。这种方式使我们可以即获得窗口递增计算的优势,又可以获得编写一个WindowFunction提供的额外窗口元信息。

下面的例子为我们展示了递增聚合方法如何与WindowFunction结合:

DataStream<Tuple2<String,
Long>> input = ...;

//
for folding incremental computation

input
  .keyBy(<key
selector>)
  .window(<window
assigner>)
  .apply(<initial
value>, new MyFoldFunction(),
new
MyWindowFunction());

//
for reducing incremental computation

input
  .keyBy(<key
selector>)
  .window(<window
assigner>)
  .apply(new
MyReduceFunction(), new MyWindowFunction());

四、处理迟到数据

在处理事件时间窗口时,可能会发生element迟到的情况,即Flink用来持续跟踪事件时间进展的watermark已经晚于到达element所属的窗口的结束时间戳了的情况。有关event time和其中迟到element等等Flink如何处理event time的详细讨论,请见event timelate element

你可以定义一个带窗口transformation如何处理迟到element以及允许的迟到时间(lateness)。相关的参数为allowed
lateness,该参数定义了element最多可以迟到多长时间。对于在allowed
lateness之内到达的element,Flink仍然会将它们放入窗口中并且考虑到计算结果之内。而在allowed
lateness之外到达的element则将被抛弃。Flink同样保证一旦Watermark超过窗口结束时间加上allowed
lateness,由窗口Operation持有的所有状态都将进入垃圾回收。

默认地,allowed
lateness设置为0,即在watermark之后到达的element将会被抛弃。你可以通过以下方式定义allow
lateness:

DataStream<T>
input = ...;

input
  .keyBy(<key
selector>)
  .window(<window
assigner>)
  .allowedLateness(<time>)
  .<windowed transformation>(<window
function>);

注意,当使用GlobalWindows的window
assigner时,没有数据会变为迟到数据,因为全局窗口的结束时间戳为Long.MAX_VALUE

五、Triggers

一个Trigger决定着窗口(由WindowAssigner赋值)什么时候准备好由window
function处理。trigger观察element如何加入窗口中,并且持续跟踪processing
time和event time的进展。一旦一个trigger决定窗口已准备好处理,它就会被触发。这是Window
Operation获取当前处于窗口中的数据,并且将它们传递给window
function来产生处于触发状态的(firing)窗口的输出的信号。

除了GlobalWindows,每个WindowAssigner都带有一个默认trigger来适用于绝大多数用例。例如,TumblingEventTimeWindows拥有EventTimeTrigger作为默认trigger,该trigger简单地在watermark超过窗口的结束时间时触发。

你可以通过使用给定Trigger类来调用trigger()方法定义所用的trigger。一个带窗口transformation的全貌大致如下:

DataStream<T>
input = ...;

input
  .keyBy(<key
selector>)
  .window(<window
assigner>)
  .trigger(<trigger>)
  .<windowed transformation>(<window
function>);

Flink自带一些开箱即用的trigger:包括上面提到的EventTimeTrigger,基于由watermark衡量的event time的进展来决定是否触发;还有ProcessingTimeTrigger,与EventTimeTrigger大致一样,但基于processing
time;最后是CountTrigger,在一个窗口的element数量溢出给定界限时触发。

注意,通过使用trigger()方法定义一个trigger,你将重写WindowAssigner的默认trigger。例如,若你为TunmblingEventTimeWindows定义CountTrigger为trigger,窗口将不会基于时间进展触发,而仅仅依靠计数结果来触发。在当前版本下,若你想要同时对时间和计数都做出响应,你只能自定义trigger。

内部Trigger
API在当前版本仍然处于测试阶段,但如果你想要编写自定义trigger,请检出(check
out)该代码。Trigger.java

六、non-keyed
windowing

你同样可以在定义一个带窗口transformation时忽略KeyBy()方法,该方式将使Flink无法并行处理各自不同key的窗口,本质上将transformation变成了一个非并行的Operation。

警告:正如本文开始介绍中提到的,由于non-keyed
windows不能分各自key独立计算,所以有着无法在集群上分布式运行的缺陷,这将带来一些性能上的影响。

一个带non-keyed
window的transformation的基本结构如下代码所示:

DataStream<T>
input = ...;

input
  .windowAll(<window
assigner>)
  .<windowed transformation>(<window
function>);

Flink Program Guide (6) -- 窗口 (DataStream API编程指导 -- For Java)的更多相关文章

  1. Flink Program Guide (2) -- 综述 (DataStream API编程指导 -- For Java)

    v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...

  2. Flink Program Guide (10) -- Savepoints (DataStream API编程指导 -- For Java)

    Savepoint 本文翻译自文档Streaming Guide / Savepoints ------------------------------------------------------ ...

  3. Flink Program Guide (8) -- Working with State :Fault Tolerance(DataStream API编程指导 -- For Java)

    Working with State 本文翻译自Streaming Guide/ Fault Tolerance / Working with State ---------------------- ...

  4. Flink Program Guide (3) -- Event Time (DataStream API编程指导 -- For Java)

    Event Time 本文翻译自DataStream API Docs v1.2的Event Time ------------------------------------------------ ...

  5. Flink Program Guide (7) -- 容错 Fault Tolerance(DataStream API编程指导 -- For Java)

    false false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {mso-style-n ...

  6. Flink Program Guide (5) -- 预定义的Timestamp Extractor / Watermark Emitter (DataStream API编程指导 -- For Java)

    本文翻译自Pre-defined Timestamp Extractors / Watermark Emitter ------------------------------------------ ...

  7. Flink Program Guide (4) -- 时间戳和Watermark生成(DataStream API编程指导 -- For Java)

    时间戳和Watermark生成 本文翻译自Generating Timestamp / Watermarks --------------------------------------------- ...

  8. Flink Program Guide (1) -- 基本API概念(Basic API Concepts -- For Java)

    false false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {mso-style-n ...

  9. Flink Program Guide (9) -- StateBackend : Fault Tolerance(Basic API Concepts -- For Java)

    State Backends 本文翻译自文档Streaming Guide / Fault Tolerance / StateBackend ----------------------------- ...

随机推荐

  1. Windows环境搭建Red5流媒体服务器指南(转)

    Windows环境搭建Red5流媒体服务器指南 Windows环境搭建Red5流媒体服务器指南 测试环境:Windows 7 一.   下载安装程序 red5-server 下载地址 https:// ...

  2. HDU 5729 - Rigid Frameworks

    题意:    对于一个由n*m个1*1的菱形组成可任意扭曲的矩形(姑且这么说),求添加斜线*(两种)让菱形变成正方形,使得整个矩形固定且无法扭曲的方案数. 分析:    n*m的矩形有如下性质:( 平 ...

  3. 安装4.x版本的express开发框架

    错误版本(未更新前的安装方法,更新后就不是这么安装了,好多网上的教程还是这种方法,所以这里先写明了,旧版这里是可以通过的,但是新版4.x就不行了,请用分割线下边的方法)   本文演示在Linux上安装 ...

  4. 基本语法 - PHP手册笔记

    PHP标记 如果文件内容是纯PHP代码,最好在文件末尾删除PHP结束标记.这可以避免在PHP结束标记之后万一意外加入了空格或者换行符,会导致PHP开始输出这些空白,而脚本中此时并无输出的意图.当然PH ...

  5. netty实现消息转发服务

    1.结构图 2.消息服务器 消息服务器(SNS)由Http Netty Server(HNS)和WebSocket Netty Server(WNS)组成.HNS采用Netty Http+XML协议栈 ...

  6. Hibernate 体系结构简述

    SessionFactory: Hibernate的关键对象,它是单个数据库映射关系经过编译后的内存镜像,同时它是线程安全的.它是生成Session的工厂,本身需要依赖于ConnectionProvi ...

  7. Nginx 变量漫谈(三)

    也有一些内建变量是支持改写的,其中一个例子是 $args. 这个变量在读取时返回当前请求的 URL 参数串(即请求 URL 中问号后面的部分,如果有的话 ),而在赋值时可以直接修改参数串.我们来看一个 ...

  8. Android 模式对话框提示Dialog

    1.先写一个Dialog类   CustomDialog package com.example.heng.adtest; import android.app.AlertDialog; import ...

  9. js里面“===”与“==”的区别

    首先,== equality 等同,=== identity 恒等. ==, 两边值类型不同的时候,要先进行类型转换,再比较. ==,不做类型转换,类型不同的一定不等.  下面分别说明: 先说 === ...

  10. JVM调优的几种策略(转)

    JVM参数调优是一个很头痛的问题,可能和应用有关系,别人说可以的对自己不一定管用.下面是本人一些JVM调优的实践经验,希望对读者能有帮助,环境LinuxAS4,resin2.1.17,JDK6.0,2 ...