Flink 用户电商行为分析项目

Flink 用户电商行为分析

1. 实时统计分析
1. 1 热门商品统计
  • 需求描述:每隔5分钟 实时展示1小时内的该网站的热门商品的TopN

  • 展示的数据形式:

    时间窗口信息:

    NO 1:商品ID+浏览次数1

    NO 2:商品ID+浏览次数2

    NO 1.商品ID+浏览次数3

  • 实现思路:

      1. 因为最终要窗口信息+商品ID 所有keyBy后需要全窗口函数 这样才能拿到窗口时间+key
      1. 而且需要浏览次数 所以需要增量聚合函数 keyBy聚合后来一条数据增量聚合一条 拿到浏览次数
      1. 以上1 2步骤后只能拿到 一个商品的浏览次数 所以为了拿到1小时内的 根据时间窗口keyBy 使用processFunction 窗口内的商品保存到ListStat中 定时器到达窗口截止时间 输出ListStat的数据
  • 代码


/**
 * 做什么 :统计一小时内热门商品 5分钟更新一次结果
 * 怎么做:
 * 1.既然输出1小时内商品信息,即输出历史数据,且每隔5分钟触发一次 即到达窗口结束的时候触发一次
 * 输出5分钟内保存的状态信息
 * 输出: 窗口结束时间 商品ID 热门数
 * <p>
 * 2 那么就要统计数出商品结束时间 商品ID 热门数
 * 热门数:增量聚合函数
 * 结束时间+商品ID:全窗口
 * <p>
 * 输出结果:
 * 窗口结束时间:2017-11-26 12:20:00.0
 * 窗口内容:
 * NO 1: 商品ID = 2338453 热门度 = 27
 * NO 2: 商品ID = 812879 热门度 = 18
 * NO 3: 商品ID = 4443059 热门度 = 18
 * NO 4: 商品ID = 3810981 热门度 = 14
 * NO 5: 商品ID = 2364679 热门度 = 14
 */
public class HotItemsPractise {
   

    public static void main(String[] args) throws Exception {
   
        //    1. 环境准备
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
        env.setParallelism(1);
        DataStreamSource<String> inputStream = env.readTextFile("/Users/liangfangwei/IdeaProjects/flinkUserAnalays/data_file/UserBehavior.csv");
        //2. 准备数据源
        DataStream<ItemBean> filterStream = inputStream.map(line -> {
   
            String[] split = line.split(",");
            return new ItemBean(Long.parseLong(split[0]), Long.parseLong(split[1]), Integer.parseInt(split[2]), split[3], Long.parseLong(split[4]));

        }).filter(item -> "pv".equals(item.getBehavior()));
       //3. 收集一个商品的聚合结果
        DataStream<ItemViewCount> windowsResult = filterStream.assignTimestampsAndWatermarks(new AscendingTimestampExtractor<ItemBean>() {
   
            @Override
            public long extractAscendingTimestamp(ItemBean element) {
   
                return element.getTimestamp() * 1000L;
            }
        }).keyBy("itemId")
                .timeWindow(Time.hours(1), Time.minutes(5))
                .aggregate(new MyAggreateCount(), new MyAllWindowsView());
        //4. 收集一小时的聚合结果

        SingleOutputStreamOperator<String> windowEnd = windowsResult
                .keyBy("windowEnd")
                .process(new ItemHotTopN(5));
        windowEnd.print();

        env.execute("HotItemsPractise");
    }

    /**
     * 窗口函数 增量聚合 为了拿到同个商品的浏览次数
     */
    public static class MyAggreateCount implements AggregateFunction<ItemBean, Long, Long> {
   

        @Override
        public Long createAccumulator() {
   
            return 0L;
        }

        @Override
        public Long add(ItemBean value, Long accumulator) {
   
            return accumulator + 1L;
        }

        @Override
        public Long getResult(Long accumulator) {
   
            return accumulator;
        }

        @Override
        public Long merge(Long a, Long b) {
   
            return null;
        }
    }
    /**
     * 全函数:输入值是增量聚合的结果+key, 为了拿到时间窗口信息 窗口的截止时间+商品ID
     */
    public static class MyAllWindowsView implements WindowFunction<Long, ItemViewCount, Tuple, TimeWindow> {
   

        /**
         * @param tuple
         * @param window
         * @param input
         * @param out
         * @throws Exception
         */
        @Override
        public void apply(Tuple tuple, TimeWindow window, Iterable<Long> input, Collector<ItemViewCount> out) throws Exception {
   
            long windowEnd = window.getEnd();
            long count = input.iterator().next();
            long itemId = tuple.getField(0);

            out.collect(new ItemViewCount(itemId, windowEnd, count));
        }
    }

    /**
     * 将一小时内的商品 保存起来 时间窗口到了排序输出TopN
     */
    public static class ItemHotTopN extends KeyedProcessFunction<Tuple, ItemViewCount, String> {
   
        ListState<ItemViewCount> itemViewCountListState;
        private int topN;

        public ItemHotTopN(int topN) {
   
            this.topN = topN;
        }

        @Override
        public void open(Configuration parameters) throws Exception {
   
            itemViewCountListState = getRuntimeContext().getListState(new ListStateDescriptor<ItemViewCount>("itemViewCount", ItemViewCount.class));
        }

        @Override
        public void processElement(ItemViewCount itemViewCount, Context ctx, Collector<String> out) throws Exception {
   
            itemViewCountListState.add(itemViewCount);
            ctx.timerService().registerEventTimeTimer(itemViewCount.getWindowEnd() + 1L);
        }

        @Override
        public void onTimer(long timestamp, OnTimerContext ctx, Collector<String> out) throws Exception {
   
            // ListState转为ArrayList
            ArrayList<ItemViewCount> arraylist = Lists.newArrayList(itemViewCountListState.get().iterator());

            arraylist.sort(new Comparator<ItemViewCount>() {
   
                @Override
                public int compare(ItemViewCount o1, ItemViewCount o2) {
   
                    return o2.getCount().intValue() - o1.getCount().intValue();
                }
            });
            StringBuilder resultStringBuilder = new StringBuilder();
            resultStringBuilder.append("===================================" + "\n");
            resultStringBuilder.append("窗口结束时间:").append(new Timestamp(timestamp).toString()).append("\n");

            for (int i = 0; i < Math.min(topN, arraylist.size()); i++) {
   

                resultStringBuilder
                        .append("NO ")
                        .append(i + 1)
                        .append(": 商品ID = ")
                        .append(arraylist.get(i).getItemId())
                        .append(" 热门度 = ")
                        .append(arraylist.get(i).getCount())
                        .append("\n");

            }
            resultStringBuilder.append("===================================\n");
            out.collect(resultStringBuilder.toString());
            Thread.sleep(1000L);

        }

    }
}

1. 2 热门页面统计
  • 需求 :每隔5分钟输出一小时内浏览的热门页面
  • 输出结果展示:

窗口结束时间:2015-05-18 13:08:50.0

NO 1: 页面URL = /blog/tags/puppet?flav=rss20 热门度 = 11
NO 2: 页面URL = /projects/xdotool/xdotool.xhtml 热门度 = 5
NO 3: 页面URL = /projects/xdotool/ 热门度 = 4
NO 4: 页面URL = /?flav=rss20 热门度 = 4
NO 5: 页面URL = /robots.txt 热门度 = 4

  • 实现思路:和上一个不同的是 该数据源中的数据的时间非增量

    • 怎么保证保证乱序数据不丢
      • 1.所以要设置watermark与数据源之间的乱序程度
      • 2.设置一定的窗口延迟关闭时间 在初始的时间窗口到了 先聚合数据 后续再来属于该窗口的数据 来一条计算一条输出一条
      • 3.再有迟到的数据 则直接扔到侧输出流中
    • 怎么保证后续迟到的数据 来一条覆盖前面的数据
      • 1 先开窗增量聚合 再全窗口聚合 再根据窗口截止时间分组
      • 2 根据时间的截止窗口开窗key by 收集窗口截止时间内的所有数据 排序输出
      • 3 如果后续再来了延迟数据 需要更新之前的结果。所以把之间的数据存咋mapstat中 key为 页面url value为输出结果
  • 代码

    /**
     *
     * -                   _ooOoo_
     * -                  o8888888o
     * -                  88" . "88
     * -                  (| -_- |)
     * -                   O\ = /O
     * -               ____/`---'\____
     * -             .   ' \\| |// `.
     * -              / \\||| : |||// \
     * -            / _||||| -:- |||||- \
     * -              | | \\\ - /// | |
     * -            | \_| ''\---/'' | |
     * -             \ .-\__ `-` ___/-. /
     * -          ___`. .' /--.--\ `. . __
     * -       ."" '< `.___\_<|>_/___.' >'"".
     * -      | | : `- \`.;`\ _ /`;.`/ - ` : | |
     * -        \ \ `-. \_ __\ /__ _/ .-` / /
     * ======`-.____`-.___\_____/___.-`____.-'======
     * `=---='
     * .............................................
     * 佛祖保佑             永无BUG
     * <p>
     * 需求
     * 每5分钟输出一次1小时之内排名前5的页面
     *  小时统计一次结果 ,即开窗是一小时 收集1小时内的统计结果,按照窗口结束时间输出窗口内的结果。窗口的滑动步长设置为5min
     */
    
    public class HotPages {
         
    
        public static void main(String[] args) throws Exception {
         
            StreamExecutionEnvironment executionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment();
            executionEnvironment.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
            executionEnvironment.setParallelism(1);
    
            DataStreamSource<String> stringDataStreamSource = executionEnvironment.readTextFile("/Users/liangfangwei/IdeaProjects/flinkUserAnalays/data_file/apache.log");
    
            SimpleDateFormat simpleFormatter = new SimpleDateFormat("dd/MM/yyyy:HH:mm:ss");
    
            OutputTag<ApacheLogEvent> lateTag = new OutputTag<ApacheLogEvent>("late_date") {
         
            };
    
            DataStream<PageViewCount> streamPageViewCount = stringDataStreamSource.map(line -> {
         
                String[] s = line.split(" ");
                // 日期转时间戳
                Long timestamp = simpleFormatter.parse(s[3]).getTime();
                return new ApacheLogEvent(s[0], s[1], timestamp, s[
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如今的大数据技术应用场景,对实时性的要求已经越来越高。作为新一代大数据流处理框架,由于非常好的实时性,Flink独树一帜,在近些年引起了业内极大的兴趣和关注。Flink能够提供毫秒级别的延迟,同时保证了数据处理的低延迟、高吞吐和结果的正确性,还提供了丰富的时间类型和窗口计算、Exactly-once 语义支持,另外还可以进行状态管理,并提供了CEP(复杂事件处理)的支持。Flink在实时分析领域的优势,使得越来越多的公司开始将实时项目Flink迁移,其社区也在快速发展壮大。目前,Flink已经成为各大公司实时领域的发力重点,特别是国内以阿里为代表的一众大厂,都在全力投入,不少公司为Flink社区贡献了大量源码。如今Flink已被很多人认为是大数据实时处理的方向和未来,很多公司也都在招聘和储备了解掌握Flink的人才。本教程将Flink理论与电商数据分析项目实战并重,对Flink基础理论知识做了系统的梳理和阐述,并通过电商用户行为分析的具体项目用多个指标进行了实战演练。为有志于增加大数据项目经验、扩展流式处理框架知识的工程师提供了学习方式。二、教程内容和目标本教程主要分为两部分:第一部分,主要是Flink基础理论的讲解,涉及到各种重要概念、原理和API的用法,并且会有大量的示例代码实现;第二部分,以电商作为业务应用场景,以Flink作为分析框架,介绍一个电商用户行为分析项目的开发实战。通过理论和实际的紧密结合,可以使学员对Flink有充分的认识和理解,在项目实战中对Flink和流式处理应用的场景、以及电商分析业务领域有更深刻的认识;并且通过对流处理原理的学习和与批处理架构的对比,可以对大数据处理架构有更全面的了解,为日后成长为架构师打下基础。三、谁适合学1、有一定的 Java、Scala 基础,希望了解新的大数据方向的编程人员2、有 Java、Scala 开发经验,了解大数据相关知识,希望增加项目经验的开发人员3、有较好的大数据基础,希望掌握Flink及流式处理框架的求职人员
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值