【学习笔记 — Flink 处理迟到数据(★)】

Flink 处理迟到数据(★)
  • 处理迟到数据之前首先了解Lambda架构

    • Lambda架构的实现是:一个批处理器、一个流处理器。流处理器首先实时输出近似正确的结果(因为乱序流,可能导致流处理结果不准确,所以是近似正确),然后当批处理器处理处理完后,把最终结果更新为批处理的结果。
  • Flink处理迟到数据的实现,其实是相当于Lambda架构的流程实现。

  • 只不过,Flink实现Lambda架构的流程更为简单,只不过是调用几个API而已。

  • **处理迟到数据代码实现:**统计每个url的访问量。

    • 分析:

      • 1.首先添加Socket数据源,实现乱序流的输入

        • env.socketTextStream("master", 7777);
          
      • 2.实现乱序流水位线生成策略,延迟时间设为2秒

        • WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(2))
          
      • 3.分区、开窗

        • //分区
          .keyBy(data -> data.url)
          //滑动事件时间窗口,大小为10秒                .window(TumblingEventTimeWindows.of(Time.seconds(10)))
          
      • 4.设置最大延迟时间

        • .window(TumblingEventTimeWindows.of(Time.seconds(10)))
          //4.设置最大延迟时间,1分钟
          .allowedLateness(Time.minutes(1))
          
      • 5.创建标签,开启侧输出流

        • 注意:由于泛型擦除,要把标签定义成匿名内部类的形式。

        • //5.创建标签,**注意**:由于泛型擦除,要把标签定义成匿名内部类的形式。
          OutputTag<Event> outputTag = new OutputTag<Event>("later") {};
          .allowedLateness(Time.minutes(1))
          //5.开启侧输出流
          .sideOutputLateData(outputTag)
          
      • 6.调用aggregate方法,传入AggregateFunction和ProcessWindowFunction

        • .sideOutputLateData(outputTag)
          //6.调用aggregate方法,传入AggregateFunction和ProcessWindowFunction
          .aggregate(new MyAggregateFunction(), new MyProcessWindowFunction());
          
      • 7.获取侧输出流

        • //7.获取侧输出流
          streamAggregate.getSideOutput(outputTag);
          
    • 完整代码

      • package finkDemo.Watermark_Windows;
        
        //导入自定义POJO类:Event、UrlViewCount
        import finkDemo.Watermark_Windows.Windows.WindowsFunction.Case.UrlViewCount;
        import finkDemo.Watermark_Windows.Windows.WindowsFunction.IncrementalAggregateFunction.Event;
        
        import org.apache.flink.api.common.eventtime.SerializableTimestampAssigner;
        import org.apache.flink.api.common.eventtime.WatermarkStrategy;
        import org.apache.flink.api.common.functions.AggregateFunction;
        import org.apache.flink.api.common.functions.MapFunction;
        import org.apache.flink.streaming.api.datastream.DataStream;
        import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
        import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
        import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
        import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
        import org.apache.flink.streaming.api.windowing.time.Time;
        import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
        import org.apache.flink.util.Collector;
        import org.apache.flink.util.OutputTag;
        
        import java.time.Duration;
        
        /**
         * 实际应用中Flink处理迟到数据,就相当于实现了Lambda架构的流程
         *
         *
         * 案例:统计每个url访问量
         */
        public class ProcessLaterData {
            public static void main(String[] args) throws Exception {
                StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
                env.setParallelism(1);
        
                //设置水位线生成周期:100ms
                env.getConfig().setAutoWatermarkInterval(100);
        
                //1.首先添加Socket数据源,实现乱序流的输入
                //nc端输入:Marry ./pid=1 2000等
                SingleOutputStreamOperator<Event> streamSource = env.socketTextStream("master", 7777)
                        //转换数据结构为Event
                        .map(new MapFunction<String, Event>() {
                            @Override
                            public Event map(String value) throws Exception {
                                //切分nc端输入的字符串
                                String[] split = value.split(" ");
                                //返回Event
                                return new Event(split[0].trim(), split[1].trim(), Long.valueOf(split[2].trim()));
                            }
                        });
        
                //2.实现乱序流水位线生成策略,延迟时间设为2秒
                SingleOutputStreamOperator<Event> streamWatermark = streamSource
                        .assignTimestampsAndWatermarks(
                                //创建乱序流水位线生成器,延迟时间为 2秒
                                WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(2))
                                        //提取、分配时间戳
                                        .withTimestampAssigner(new SerializableTimestampAssigner<Event>() {
                                            @Override
                                            public long extractTimestamp(Event element, long recordTimestamp) {
                                                //提取时间戳
                                                return element.timestamps;
                                            }
                                        })
                        );
        
                streamWatermark.print("data");
        
                //5.创建标签,**注意**:由于泛型擦除,要把标签定义成匿名内部类的形式。
                OutputTag<Event> outputTag = new OutputTag<Event>("later") {};
                //3.分区、开窗
                SingleOutputStreamOperator<UrlViewCount> streamAggregate = streamWatermark
                        //分区
                        .keyBy(data -> data.url)
                        //滑动事件时间窗口,大小为10秒
                        .window(TumblingEventTimeWindows.of(Time.seconds(10)))
                        //4.设置最大延迟时间,1分钟
                        .allowedLateness(Time.minutes(1))
                        //5.开启侧输出流
                        .sideOutputLateData(outputTag)
                        //6.调用aggregate方法,传入AggregateFunction和ProcessWindowFunction
                        .aggregate(new MyAggregateFunction(), new MyProcessWindowFunction());
        
                streamAggregate.print("result");
                //7.获取侧输出流
                DataStream<Event> streamSideOutput = streamAggregate.getSideOutput(outputTag);
                streamSideOutput.print("later");
        
        
                env.execute();
            }
        
            //自定义AggregateFunction
            public static class MyAggregateFunction implements AggregateFunction<Event,Long,Long>{
                @Override
                public Long createAccumulator() {
                    return 0L;
                }
        
                @Override
                public Long add(Event value, Long accumulator) {
                    return accumulator+1L;
                }
        
                @Override
                public Long getResult(Long accumulator) {
                    //返回url的访问量
                    return accumulator;
                }
        
                //由于不是会话窗口,可以不用写merge方法
                @Override
                public Long merge(Long a, Long b) {
                    return null;
                }
            }
        
            //自定义ProcessWindowFunction
            //由于这里的IN是getResult方法的返回值,所以IN类型为Long
            public static class MyProcessWindowFunction extends ProcessWindowFunction<Long, UrlViewCount,String, TimeWindow>{
                @Override
                public void process(String url, ProcessWindowFunction<Long, UrlViewCount, String, TimeWindow>.Context context, Iterable<Long> elements, Collector<UrlViewCount> out) throws Exception {
                    //窗口开启时间
                    long start = context.window().getStart();
                    //窗口关闭时间
                    long end = context.window().getEnd();
                    //获取每个url的访问量
                    Long count = elements.iterator().next();
        
                    //分装为UrlViewCount类型输出
                    out.collect(new UrlViewCount(url,count,start,end));
                }
            }
        }
        
    • 结果分析
      在这里插入图片描述

      • 第一个result结果输出,count = 3
        • 第一个窗口的范围为[0 , 10000)。因为窗口默认右边值为 end-1,由于窗口大小为10000,所以end-1=9999
        • 前面的数据,只有三条数据的时间戳,在[0 , 10000)之内,所以count=3
        • 由于数据时间戳为12000
        • 延迟时间为2秒
        • 水位线的时间戳为10000
        • 水位线推动窗口,触发结果输出。由于最大延迟时间,此时第一个窗口没有关闭。
      • 第二个result结果输出,count = 4
        • 由于最大延迟时间为,1分钟,也就是60000毫秒,所以第一个窗口延迟关闭60000毫秒
        • 由于前面一条数据的时间戳为4000,在第一个窗口范围之内,所以被第一个窗口收集,并更新输出的结果,为count=4
      • 第三个result结果,count = 2
        • 第二个窗口范围为[ 10000, 20000)
        • 前面数据的时间戳,在[10000 , 20000)之内的数据共有两条,所以count=2
      • 第一个later,count=1
        • 在时间戳为72000的数据的推动下,水位线为70000=最大延迟时间+end1,推动了第一个窗口的关闭、销毁。
        • 最后一条数据的时间戳为2000,但是第一个窗口已经关闭,所以该数据会流入侧输出流,并输出。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值