Flink开发-全局窗口GlobalWindows

全局窗口没有结束的边界,使用的Trigger(触发器)是NeverTrigger。如果不对全局窗口指定一个触发器,窗口是不会触发计算的。

1.Non-Keyed Count Windows

是按照窗口中接收到数据的条数划分窗口的,跟时间无关。Non-Keyed Windows,就仅有一个全局窗口。Count Windows属于Global Windows并指定了CountTrigger。如果没有达到指定的条数,窗口不会被触发执行。

1.1 aggregates增量聚合

    public static void main(String[] args) throws Exception {
        //CountWindowAll是GlobalWindow的一种
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        DataStreamSource<String> socketStream = env.socketTextStream("localhost", 8888);
        SingleOutputStreamOperator<Integer> mapStream = socketStream.map(Integer::parseInt);
        //划分Non-Keyed countWindowAll,并行度为1
        AllWindowedStream<Integer, GlobalWindow> windowAll = mapStream.countWindowAll(5);
        //把窗口数据进行聚合
        SingleOutputStreamOperator<Integer> sum = windowAll.sum(0);
        sum.print();
        env.execute("");
    }

输入内容:

C:\Users\zhibai>nc -lp 8888
1
2
3
4
5
6
7
8
9
10

输出结果:

4> 15
5> 40

1.2 reduce增量聚合

    public static void main(String[] args) throws Exception {
        //local模式默认的并行度是当前机器的逻辑核数
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        DataStreamSource<String> socketStream = env.socketTextStream("localhost", 8888);
        SingleOutputStreamOperator<Integer> mapStream = socketStream.map(Integer::parseInt);
        //划分Non-Keyed countWindowAll,并行度为1
        AllWindowedStream<Integer, GlobalWindow> windowAll = mapStream.countWindowAll(5);
        //把窗口数据进行增量聚合,每次数据流入都会计算结果,内存中只保留中间状态,效率更高更节省资源。
        SingleOutputStreamOperator<Integer> reduce = windowAll.reduce(new ReduceFunction<Integer>() {
            @Override
            public Integer reduce(Integer t1, Integer t2) throws Exception {
                return t1 + t2;
            }
        });
        reduce.print();
        env.execute("");
    }

输入内容:

C:\Users\zhibai>nc -lp 8888
1
2
3
4
5
6
7
8
9
10

输出结果:

1> 15
2> 40

1.3 apply全量聚合

程序运行时将窗口中的数据先在window state中存起来,当满足触发条件后再将状态中的数据取出来进行计算。

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        DataStreamSource<String> socketStream = env.socketTextStream("localhost", 8888);
        SingleOutputStreamOperator<Integer> mapStream = socketStream.map(Integer::parseInt);
        //划分Non-Keyed countWindowAll,并行度为1
        AllWindowedStream<Integer, GlobalWindow> windowAll = mapStream.countWindowAll(5);
        SingleOutputStreamOperator<Integer> apply = windowAll.apply(new AllWindowFunction<Integer, Integer, GlobalWindow>() {
            @Override
            public void apply(GlobalWindow window, Iterable<Integer> values, Collector<Integer> out) throws Exception {
                Integer sum = 0;
                for (Integer value : values) {
                    sum += value;
                }
                out.collect(sum);
            }
        });
        apply.print();
        env.execute("");
    }

输入内容:

C:\Users\zhibai>nc -lp 8888
1
2
3
4
5
6
7
8
9
10

输出结果:

4> 15
5> 40

因为apply是对全量数据进行处理我们也可以利用这一特点,对窗口内的数据进行排序等操作。

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        DataStreamSource<String> socketStream = env.socketTextStream("localhost", 8888);
        SingleOutputStreamOperator<Integer> mapStream = socketStream.map(Integer::parseInt);
        //划分Non-Keyed countWindowAll,并行度为1
        AllWindowedStream<Integer, GlobalWindow> windowAll = mapStream.countWindowAll(5);
        SingleOutputStreamOperator<Integer> apply = windowAll.apply(new AllWindowFunction<Integer, Integer, GlobalWindow>() {
            @Override
            public void apply(GlobalWindow window, Iterable<Integer> values, Collector<Integer> out) throws Exception {
                ArrayList<Integer> lst = new ArrayList<>();
                for (Integer value : values) {
                    lst.add(value);
                }
                lst.sort(new Comparator<Integer>() {
                    @Override
                    public int compare(Integer o1, Integer o2) {
                        //return o1 - o2;
                        return Integer.compare(o1,o2);
                    }
                });
                for (Integer i : lst) {
                    out.collect(i);
                }
            }
        });
        apply.print().setParallelism(1);
        env.execute("");
    }

输入内容:

C:\Users\zhibai>nc -lp 8888
5
4
3
2
1

输出结果:

1
2
3
4
5

2.Keyed Count Windows

全局窗口将key相同的数据都分配到一个单独的窗口中,每一种key对应一个全局窗口,多个全局窗口之间是相互独立的,每个组可以分别触发,不需要等待所有组都满足结果再触发。

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        DataStreamSource<String> socketStream = env.socketTextStream("localhost", 8888);
        SingleOutputStreamOperator<Tuple2<String, Integer>> wordAndOne = socketStream.map(new MapFunction<String, Tuple2<String, Integer>>() {
            @Override
            public Tuple2<String, Integer> map(String s) throws Exception {
                String[] fields = s.split(" ");
                return Tuple2.of(fields[0], Integer.parseInt(fields[1]));
            }
        });
        KeyedStream<Tuple2<String, Integer>, String> keyedStream = wordAndOne.keyBy(new KeySelector<Tuple2<String, Integer>, String>() {
            @Override
            public String getKey(Tuple2<String, Integer> s) throws Exception {
                return s.f0;
            }
        });
        SingleOutputStreamOperator<Tuple2<String, Integer>> reduce = keyedStream.countWindow(3).reduce(new ReduceFunction<Tuple2<String, Integer>>() {
            @Override
            public Tuple2<String, Integer> reduce(Tuple2<String, Integer> value1, Tuple2<String, Integer> value2) throws Exception {
                value1.f1 = value1.f1 + value2.f1;
                return value1;
            }
        });
        reduce.print();
        env.execute("");
    }

输入内容:

C:\Users\zhibai>nc -lp 8888
hadoop 2
flink 1
spark 3
hadoop 3
hadoop 1
flink 2
spark 4
flink 4
spark 7

输出结果:

8> (hadoop,6)
7> (flink,7)
1> (spark,14)
<think>好的,我现在需要帮助用户了解Flink的时间窗口开窗。首先,用户之前问过如何设置队列最大长度,现在转向了Flink的时间窗口,可能是在处理实时数据流时需要窗口功能,比如统计某段时间的数据。我需要先确认用户的使用场景,可能是实时数据处理或流计算,但用户没有详细说明,所以需要覆盖基础概念和常见窗口类型。 接下来,用户可能对时间窗口的基本概念不太熟悉,所以需要从基础讲起,比如时间窗口的作用和类型。然后,用户可能想知道如何具体在Flink中实现这些窗口,包括代码示例。需要分清楚处理时间、事件时间和摄入时间的区别,因为这是Flink中时间语义的重要部分。 用户可能还需要知道不同的窗口类型,比如滚动窗口、滑动窗口和会话窗口,以及它们的应用场景。需要提供每种窗口的定义、特点和使用方法,最好有代码片段帮助理解。此外,用户可能对窗口的生命周期和触发器感兴趣,但根据问题,可能暂时不需要深入这部分,但可以简要提及。 另外,用户之前的问题涉及队列长度设置,可能现在在处理流数据时遇到了窗口相关的性能或配置问题,所以需要强调窗口配置的注意事项,比如时间语义的选择、延迟数据处理和水位线生成。这部分内容能帮助用户正确配置窗口,避免常见错误。 需要检查是否有必要对比不同窗口类型的适用场景,帮助用户根据需求选择合适的窗口。同时,生产环境的建议,比如水位线策略和延迟数据处理,对用户的实际应用可能有很大帮助,需要详细说明。 最后,总结部分要简明扼要,强调重点,让用户能快速回顾核心内容。可能还需要提醒用户根据具体业务需求调整窗口参数,并考虑性能优化。要确保回答结构清晰,步骤明确,覆盖用户可能的需求点,同时保持语言简洁易懂。</think>以下是关于Flink时间窗口开窗的详细技术解析,包含核心概念、窗口类型及实战示例: --- ### 一、时间窗口核心原理 Flink通过**时间窗口(Time Windows)**将无界数据流划分为有限的数据块进行处理。其核心要素包含: 1. **时间语义**: - $处理时间(Processing Time)$:以系统时钟为基准(最快但不可靠) - $事件时间(Event Time)$:以数据自带时间戳为基准(需要水位线机制) - $摄入时间(Ingestion Time)$:以数据进入Flink的时间为基准 2. **窗口生命周期**: ```mermaid graph LR 创建窗口-->数据分配 数据分配-->触发计算 触发计算-->窗口销毁 ``` --- ### 二、基础窗口类型详解 #### 1. 滚动窗口(Tumbling Window) $$ \text{窗口长度}=T,\ \text{窗口间无重叠} $$ ```java // 事件时间滚动窗口(5分钟) DataStream<T> input = ...; input .keyBy(<key selector>) .window(TumblingEventTimeWindows.of(Time.minutes(5))) .reduce(<ReduceFunction>); // 处理时间滚动窗口(10秒) .window(TumblingProcessingTimeWindows.of(Time.seconds(10))) ``` #### 2. 滑动窗口(Sliding Window) $$ \text{窗口长度}=T,\ \text{滑动步长}=S\ (S < T) $$ ```java // 事件时间滑动窗口(1小时窗口,15分钟滑动) .window(SlidingEventTimeWindows.of(Time.hours(1), Time.minutes(15))) // 带偏移量的滑动窗口(解决时区问题) .of(Time.hours(24), Time.hours(1), Time.hours(-8)) // UTC+8时区对齐 ``` #### 3. 会话窗口(Session Window) $$ \text{动态窗口,通过间隙(gap)分割} $$ ```java // 事件时间会话窗口(10分钟不活动则关闭) .window(EventTimeSessionWindows.withGap(Time.minutes(10))) // 动态间隙配置(根据数据特征调整) .withDynamicGap((element) -> { // 根据数据内容返回不同gap时长 return determineGap(element); }); ``` --- ### 三、高级窗口操作 #### 1. 全局窗口 + 触发器 ```java // 自定义每100个元素触发计算 .window(GlobalWindows.create()) .trigger(CountTrigger.of(100)) ``` #### 2. 延迟数据处理(Allowed Lateness) ```java .window(...) .allowedLateness(Time.minutes(5)) // 允许迟到5分钟 .sideOutputLateData(lateOutputTag) // 侧输出迟到数据 ``` #### 3. 窗口函数对比 | 函数类型 | 特点 | 适用场景 | |------------------|-----------------------------|----------------------| | ReduceFunction | 增量计算,输入输出类型一致 | 简单聚合(sum/max) | | AggregateFunction| 中间状态可维护复杂聚合 | 平均值、复杂统计 | | ProcessWindowFunction | 全量访问窗口数据 | 排序、TopN操作 | --- ### 四、水位线(Watermark)关键配置 ```java // 有序事件水位线(无乱序) .assignTimestampsAndWatermarks( WatermarkStrategy .<Event>forMonotonousTimestamps() .withTimestampAssigner((event, ts) -> event.timestamp) ); // 乱序事件水位线(最大允许延迟2秒) .assignTimestampsAndWatermarks( WatermarkStrategy .<Event>forBoundedOutOfOrderness(Duration.ofSeconds(2)) .withTimestampAssigner(...) ); ``` --- ### 五、生产环境最佳实践 1. **窗口长度选择公式**: $$ \text{窗口长度} = \frac{\text{业务周期}}{\text{精度要求系数}} $$ 示例:每分钟统计的日粒度数据: $$ \frac{24小时}{1440} = 1分钟 $$ 2. **内存控制**: ```java // 设置状态后端及TTL env.setStateBackend(new RocksDBStateBackend(...)); env.getConfig().setAutoWatermarkInterval(1000); // 1秒生成一次水位线 ``` 3. **性能调优参数**: ```shell # 调整Flink配置 taskmanager.memory.task.heap.size: 4096m taskmanager.numberOfTaskSlots: 4 parallelism.default: 8 ``` --- ### 六、典型问题排查指南 1. **窗口不触发**: - 检查水位线生成是否正常 - 验证事件时间是否远小于系统时间 - 使用`debug`模式输出水位线日志 2. **结果数据缺失**: ```java // 添加迟到数据侧输出 OutputTag<Event> lateDataTag = new OutputTag<>("late-data"){}; .sideOutputLateData(lateDataTag) ``` --- 通过合理选择窗口类型与参数配置,可以实现从秒级实时监控到天级离线统计的全场景覆盖。建议结合Flink Web UI的窗口监控指标进行动态调优。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值