flink windows窗口 应用范例

概述:

流式数据处理中,很多操作要依赖于时间属性进行,因此时间属性也是流式引擎能够保证准确处理数据的基石,window提供了一种处理无界数据的一种手段。flink的windows包含了很多的组件: 例如出发器trigger,触发器上下文triggerContext,内部状态windowState。窗口分配器windowassigner,内部时间服务器internalTimerService。其中每个组件都是winows 完成功能不可缺失的一部分。具体每个组件的作用。

1.Time
在 Flink 的流式处理中,会涉及到时间的不同概念
Event Time: 是事件创建时间。他通常由事件中时间戳描述。例如采集的日志数据中,每一条日志都会记录自己的生成时间。
ingestion Time: 是事件进入flink的时间;
**Processing Time:**被算子处理时的时间。默认的时间属性就是Processing Time。
flink 官网经典例子:flink windows窗口 应用范例_第1张图片

2、window概念:

Streaming流式计算是一种被设计用于处理无限数据集的数据处理引擎,而无限数据集是指一种不断增长的无限的数据集。而 Window 是一种切割无限数据集为有限块进行处理的手段。flink中我们可以将一个无界的数据流通过划分窗口,把某段时间的数据当作有界的数据流。然后对这部分有界数据流进行计算。( Window是无限数据流处理的核心,Window 可以将一个无限的 stream 流拆分成有限大小的"buckets")

3、window 窗口的划分:

1、windows 按照窗口驱动分为两类:
1)、CountWindow:按照指定的数据条数生成一个Window,与时间无关。(如美100个元素驱动)
2).TimeWindow:按照时间生成Window,对于 TimeWindow,可以根据窗口实现原理的不同分成三类:1.滚动窗口(Tumbling Window)无重叠、2.滑动窗口(Sliding Window)有重叠、3.会话窗口(Session Window)

window 的继承关系为 参考:https://juejin.im/post/6844903721520857102
flink windows窗口 应用范例_第2张图片
具体的应用API
flink windows窗口 应用范例_第3张图片
具体滚动、滑动、session 窗口的概念参考:官网https://ci.apache.org/projects/flink/flink-docs-release-1.9/zh/dev/stream/operators/windows.html

2、按照window窗口的使用可以分为Keyed Windows 和Non-Keyed Windows。
两者之间唯一的区别是:使用KeyBy()分组一般需要我们接分组的 .window()函数。再不使用keyBy() 分组时候后面我们应用 .windowAll(); (windowAll 默认并行度为1,并且不可设置并行度)

1)、keyed windows(分組窗口)使用

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/fold/apply()      <-  required: "function"
      [.getSideOutput(...)]      <-  optional: "output tag"

2)、Non-Keyed Windows(未分组窗口)

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/fold/apply()      <-  required: "function"
      [.getSideOutput(...)]      <-  optional: "output tag"
In the above, the commands in square brackets ([…]) are optional. This reveals that Flink allows you to customize your windowing logic in many different ways so that it best fits your needs.

在上面,方括号([…])中的命令是可选的。这表明Flink允许您以多种不同方式自定义窗口逻辑,从而使其最适合您的需求。而其中聚合函数又分为增量聚合和全量聚合之分

window 增量聚合函数。
窗口的增量聚合函数,指的是窗口中每进入一条数据触发一次计算。具体函数包括了 。
reduce(reduceFunction) 、aggregate(aggregateFunction) 、sum() 、min()、 max() 函数等。(注意区分 窗口触发的不同)
flink windows窗口 应用范例_第4张图片
窗口的全量聚合函数,指的是等窗口的数据对齐,才开始进行聚合计算(可以实现对窗口内的数据进行排序等需求)
如 函数: apply(windowFunction) 、process(processWindowFunction)等
processWindowFunction比windowFunction提供了更多的上下文信息,里面可以定制性开发自己需要的东西。
flink windows窗口 应用范例_第5张图片

4、应用举例

1、简单window和 windowAll 应用

 //1.Non-Keyed(未分组使用 windowAll()开启窗口)
SingleOutputStreamOperator> mapOperatorStream =
        source.map(Integer::parseInt).map(num -> Tuple2.of(num, 1)).returns(Types.TUPLE(Types.INT, Types.INT));

mapOperatorStream .windowAll()
                
//2.Keyed(已分组使用 window()开启窗口)
KeyedStream, Tuple> keyedStream = mapOperator.keyBy(0);

keyedStream.window();

2、countWindow 示例应用
按照countwindow 的使用,也可以分为 keyed 和non-keyed 两种情况。
1)、分组情况下使用
2)、未分组情况下使用
3)、还可以应用滑动窗口实现

public class CountWindowDemo {

    public static void main(String[] args) throws Exception{

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource source = env.socketTextStream("localhost", 8888);

        SingleOutputStreamOperator> mapOperator =
                source.map(Integer::parseInt).map(num -> Tuple2.of(num, 1)).returns(Types.TUPLE(Types.INT, Types.INT));

        //1.未分组,使用countWindowAll()
        AllWindowedStream, GlobalWindow> windowedStream = mapOperator.countWindowAll(5);
        
        //2.分组后,使用countWindow()
        //KeyedStream, Tuple> keyedStream = mapOperator.keyBy(0);
        //WindowedStream, Tuple, GlobalWindow> windowedStream = keyedStream.countWindow(5);

        SingleOutputStreamOperator> sum = windowedStream.sum(0);

        sum.print();
        
        env.execute("CountWindowDemo");
    }
}

3、滚动窗口(Tumbling Window)示例
将数据依据固定的窗口长度,对数据进行切片。通常用来计算窗口滚动一次,在窗口中的数据。
滚动窗口(Tumbling Window)的使用,也分为 Keyed 和 Non-Keyed 两种情况:
1.分组情况下使用 timeWindow(Time size);
2.未分组情况下使用 timeWindowAll(Time size)

public class TumblingWindowDemo {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource source = env.socketTextStream("localhost", 8888);

        SingleOutputStreamOperator> mapOperator =
                source.map(Integer::parseInt).map(num -> Tuple2.of(num, 1)).returns(Types.TUPLE(Types.INT, Types.INT));

        //1.未分组,使用timeWindowAll()
        AllWindowedStream, TimeWindow> windowedStream = mapOperator.timeWindowAll(Time.seconds(5));

        //2.分组后,使用timeWindow()
        //KeyedStream, Tuple> keyedStream = mapOperator.keyBy(0);
        //WindowedStream, Tuple, TimeWindow> windowedStream = keyedStream.timeWindow(Time.seconds(5));

        SingleOutputStreamOperator> sum = windowedStream.sum(0);

        sum.print();

        env.execute("TumblingWindowDemo");
    }
}

注意:以下两种方式等价

AllWindowedStream, TimeWindow> windowedStream = mapOperator.timeWindowAll(Time.seconds(5));

AllWindowedStream, TimeWindow> windowedStream =mapOperator.windowAll(TumblingProcessingTimeWindows.of(Time.seconds(5)));

测试结果:
未分组:不管你输入多少条数据,都对其求和输出,5 秒滚动一次。如果在 5 秒之内未输入任何数据,则不输出
已分组:对分组中的数据 5 秒滚动一次。如果有多个分组,5秒之后,求和后同步输出所有分组数据。

4、滑动窗口(Sliding Window)示例
滑动窗口是固定窗口的更广义的一种形式,滑动窗口由1.固定的窗口长度和2.滑动步长组成。通常用来计算窗口中数据的变化趋势。(滑动步长越短,绘出的曲线图就更加的平滑,而不是突兀的)

滚动窗口(Sliding Window)的使用,也分为 Keyed 和 Non-Keyed 两种情况:
     1.分组情况下使用 timeWindow(Time size,Time slide);
     2.未分组情况下使用 timeWindowAll(Time size,Time slide)
public class SlidingWindowDemo {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource source = env.socketTextStream("localhost", 8888);

        SingleOutputStreamOperator> mapOperator =
                source.map(Integer::parseInt).map(num -> Tuple2.of(num, 1)).returns(Types.TUPLE(Types.INT, Types.INT));

        //1.未分组,使用timeWindowAll()
        AllWindowedStream, TimeWindow> windowedStream = mapOperator.timeWindowAll(Time.seconds(10),Time.seconds(5));

        //2.分组后,使用timeWindow()
        //KeyedStream, Tuple> keyedStream = mapOperator.keyBy(0);
        //WindowedStream, Tuple, TimeWindow> windowedStream = keyedStream.timeWindow(Time.seconds(10),Time.seconds(5));

        SingleOutputStreamOperator> sum = windowedStream.sum(0);

        sum.print();

        env.execute("SlidingWindowDemo");
    }
}

和滑动窗口同理:如下两种方式等价

AllWindowedStream, TimeWindow> windowedStream = mapOperator.timeWindowAll(Time.seconds(10),Time.seconds(5));

AllWindowedStream, TimeWindow> windowedStream = mapOperator.windowAll(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5)));

测试结果:
设置窗口长度为10s,5s滚动一次
未分组:根据输入的数据求和,开始滑动, 5s 输出一次
已分组:对分组中的数据 5 秒滑动一次。如果有多个分组,5秒之后,求和后同步输出所有分组数据。

5、会话窗口(Session Window)示例
由一系列事件组合一个指定时间长度的timeout 间隙组成。类似web应用session,**也就是一段时间没有接收到新的数据就会生成新的窗口。**通常用于尖酸两条数据之间的间隔,如果间隔大于指定时间。前面的数据就划分为一个Window.

会话窗口(Session Window)的使用,也分为 Keyed 和 Non-Keyed 两种情况:
     1.分组情况下使用 window(ProcessingTimeSessionWindows.withGap(Time.seconds(5)));
     2.未分组情况下使用 windowAll(ProcessingTimeSessionWindows.withGap(Time.seconds(5)))
public class SessionWindowDemo {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource source = env.socketTextStream("localhost", 8888);

        SingleOutputStreamOperator> mapOperator =
                source.map(Integer::parseInt).map(num -> Tuple2.of(num, 1)).returns(Types.TUPLE(Types.INT, Types.INT));

        //1.未分组,使用 windowAll() 
        AllWindowedStream, TimeWindow> windowedStream = mapOperator.windowAll(ProcessingTimeSessionWindows.withGap(Time.seconds(5)));

        //2.分组后,使用 window()
//        KeyedStream, Tuple> keyedStream = mapOperator.keyBy(0);
//        WindowedStream, Tuple, TimeWindow> windowedStream = keyedStream.window(EventTimeSessionWindows.withGap(Time.seconds(5)));

        SingleOutputStreamOperator> sum = windowedStream.sum(0);

        sum.print();

        env.execute("SessionWindowDemo");
    }
}

测试结果:
5s 内没有数据输入时(大于指定时间没有数据阈值),执行输出结果。

注意:如果争议支持忘提出,自己会及时分析并更正

你可能感兴趣的:(flink,工作实践,大数据,window)