基于Flink的个人装扮商城群体用户画像与数据实时统计系统(五)-需求集B实现

一、需求集B有什么?

所有需求link:基于Flink的个人装扮商城群体用户画像与数据实时统计系统(二)-项目介绍与需求介绍

  需求集B是针对模拟生成的用户浏览商品的信息提出的,包括:

  • 群体用户画像之当日实时品牌偏好
    • 实时包品牌偏好
    • 实时服装品牌偏好
    • 实时鞋品牌偏好
  • 各类产品(包类、服装类、鞋类)近一分钟浏览次数统计,每10s统计一次(超喜欢的需求)
  • 群体用户画像之当日实时终端偏好

附:模拟生成的用户浏览商品的信息字段
在这里插入图片描述

二、模拟生成用户浏览商品的信息

  • 用户浏览商品的信息实体类编写:ScanProductInfo
package cn.edu.neu.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author 32098
 *
 * 用户商品浏览
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ScanProductInfo {
    /**
     * 用户ID
     */
    private String userId;
    /**
     * 商品ID
     */
    private String productId;
    /**
     * 商品类别
     */
    private String productType;
    /**
     * 浏览的商品品牌
     */
    private String brand;
    /**
     * 商品浏览的开始时间
     */
    private long beginScanTime;
    /**
     * 停留在商品的时间:ms
     */
    private long stayTime;
    /**
     * 浏览使用的终端类型
     */
    private String useType;
}

  • 用户浏览商品的信息模拟生成:ScanProductInfoSource
package cn.edu.neu.source;

import cn.edu.neu.bean.ScanProductInfo;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.source.RichParallelSourceFunction;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * @author 32098
 *
 * 模拟商品浏览数据
 */
public class ScanProductInfoSource extends RichParallelSourceFunction<ScanProductInfo> {
    private boolean keepMockData;

    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        keepMockData = true;
    }

    @Override
    public void run(SourceContext<ScanProductInfo> sourceContext) throws Exception {
        // 用户ID
        String userId;
        // 商品ID
        String productId;
        // 商品类型
        List<String> productTypeList = Arrays.asList("Clothes", "Shoes", "Bags");
        String productType = "";
        // 浏览商品开始的时间
        long scanBeginTime;
        // 停留时间
        long stayTime;
        // 用户使用的终端
        List<String> useTypeList = Arrays.asList("PC端", "小程序端", "移动端");
        String useType;
        // 用户浏览的商品品牌
        List<List<String>> brandsOfProductType = Arrays.asList(
                Arrays.asList("UNIQLO优衣库", "PEACEBIRD太平鸟", "每依站", "LEDIN乐町", "VEROMODA"),
                Arrays.asList("LINING李宁", "鸿星尔克ERKE", "ANTA安踏", "Nike耐克", "XTEP特步", "NewBalance", "鸿星尔克ERKE", "361°"),
                Arrays.asList("LV路易威登", "Prada普拉达", "Hermes爱马仕", "Gucci古驰", "Armani阿玛尼", "BALLY巴利", "Fendi芬迪", "纪梵希")
        );
        String brand;

        Random r = new Random();

        userId = RandomStringUtils.randomNumeric(3);
        productId = RandomStringUtils.randomAlphabetic(6);
        int productTypeIdx = r.nextInt(productTypeList.size());
        productType = productTypeList.get(productTypeIdx);
        List<String> brandsOfCurProType = brandsOfProductType.get(productTypeIdx);
        brand = brandsOfCurProType.get(r.nextInt(brandsOfCurProType.size()));
        stayTime = r.nextInt(5000)+500;
        scanBeginTime = System.currentTimeMillis();
        double prob = r.nextDouble();
        if(prob>0.45){
            useType = useTypeList.get(2);
        }else if(prob>0.18){
            useType = useTypeList.get(1);
        }else{
            useType = useTypeList.get(0);
        }
        sourceContext.collect(new ScanProductInfo(userId, productId, productType, brand, scanBeginTime, stayTime, useType));

        while (keepMockData) {
            for(int i=0; i<r.nextInt(20); i++){
                userId = RandomStringUtils.randomNumeric(3);
                productId = RandomStringUtils.randomAlphabetic(6);

                if((!"".equals(productType)) && r.nextDouble()<0.2){
                    productTypeIdx = r.nextInt(productTypeList.size());
                    productType = productTypeList.get(productTypeIdx);
                    brandsOfCurProType = brandsOfProductType.get(productTypeIdx);
                    brand = brandsOfCurProType.get(r.nextInt(brandsOfCurProType.size()));
                }

                stayTime = r.nextInt(5000)+500;
                scanBeginTime = System.currentTimeMillis() - stayTime;

                prob = r.nextDouble();
                if(prob>0.45){
                    useType = useTypeList.get(2);
                }else if(prob>0.18){
                    useType = useTypeList.get(1);
                }else{
                    useType = useTypeList.get(0);
                }

                sourceContext.collect(new ScanProductInfo(userId, productId, productType, brand, scanBeginTime, stayTime, useType));
            }
            Thread.sleep((r.nextInt(20)+1)*100);
        }
    }

    @Override
    public void cancel() {
        keepMockData = false;
    }
}
  • 编写 FlinkKafkaProducer,模拟用户浏览商品的信息的采集
package cn.edu.neu.kafka;

import cn.edu.neu.bean.ScanProductInfo;
import cn.edu.neu.source.ScanProductInfoSource;
import com.alibaba.fastjson.JSON;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer;

import java.util.Properties;

/**
 * @author 32098
 */
public class ScanProductInfoKafkaProducer {
    public static void main(String[] args) throws Exception {
        // 1. env:创建流式执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        // 2. source:添加自定义产生广告点击模拟数据的Source
        DataStreamSource<ScanProductInfo> advertiseClickDataStream = env.addSource(new ScanProductInfoSource());

        // 3. transformation
        SingleOutputStreamOperator<String> advertiseClickDataJsonStream = advertiseClickDataStream.map(new MapFunction<ScanProductInfo, String>() {
            @Override
            public String map(ScanProductInfo advertiseClickBean) throws Exception {
                return JSON.toJSONString(advertiseClickBean);
            }
        });

        // 4. sink to kafka
        Properties props = new Properties();
        props.setProperty("bootstrap.servers", "master:9092");
        FlinkKafkaProducer<String> kafkaSink = new FlinkKafkaProducer<>("flink_kafka", new SimpleStringSchema(), props);

        advertiseClickDataJsonStream.addSink(kafkaSink);

        // 5. execute
        env.execute();
    }
}

三、需求集B实现

  • 群体用户画像之当日实时品牌偏好
package cn.edu.neu.task.windowTask;

import cn.edu.neu.bean.ScanProductInfo;
import cn.edu.neu.bean.Statics;
import cn.edu.neu.sink.StaticsSink;
import com.alibaba.fastjson.JSON;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.ReduceFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.SlidingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.triggers.ContinuousProcessingTimeTrigger;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;

import java.util.ArrayList;
import java.util.Properties;

/**
 *
 * @author 32098
 *
 * 群体用户画像之实时品牌偏好
 */
public class BrandLikeTask {
    public static void main(String[] args) {
        Properties pros = new Properties();
        pros.setProperty("bootstrap.servers", "master:9092");
        pros.setProperty("group.id", "flink");
        pros.setProperty("auto.offset.reset","latest");
        pros.setProperty("flink.partition-discovery.interval-millis","5000");
        pros.setProperty("enable.auto.commit", "true");
        pros.setProperty("auto.commit.interval.ms", "2000");

        FlinkKafkaConsumer<String> kafkaSource = new FlinkKafkaConsumer<String>(
                "flink_kafka",
                new SimpleStringSchema(),
                pros
        );
        kafkaSource.setStartFromLatest();

        // 1. env
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        // 2. source
        DataStreamSource<String> kafkaDataStream = env.addSource(kafkaSource);

        // 3. transformation
        // to java object
        SingleOutputStreamOperator<ScanProductInfo> scanProductInfoDataStream = kafkaDataStream.map(new MapFunction<String, ScanProductInfo>() {
            @Override
            public ScanProductInfo map(String s) throws Exception {
                return JSON.parseObject(s, ScanProductInfo.class);
            }
        });

        // Exception in thread "main" org.apache.flink.api.common.functions.InvalidTypesException: The generic type parameters of 'Tuple2' are missing.
        // In many cases lambda methods don't provide enough information for automatic type extraction when Java generics are involved. An easy workaround is to use an (anonymous) class instead that implements the 'org.apache.flink.api.java.functions.KeySelector' interface.
        // Otherwise the type has to be specified explicitly using type information.
//        SingleOutputStreamOperator<Statics> resultDs = scanProductInfoDataStream.map(new MapFunction<ScanProductInfo, Tuple2<Tuple2<String, String>, Long>>() {
//            @Override
//            public Tuple2<Tuple2<String, String>, Long> map(ScanProductInfo scanProductInfo) throws Exception {
//                String productType = scanProductInfo.getProductType();
//                String brand = scanProductInfo.getBrand();
//                return Tuple2.of(Tuple2.of("brandLikeOf" + productType, brand), 1L);
//            }
//        }).keyBy(e -> e.f0).window(SlidingProcessingTimeWindows.of(Time.minutes(6), Time.minutes(1))).reduce(new ReduceFunction<Tuple2<Tuple2<String, String>, Long>>() {
//            @Override
//            public Tuple2<Tuple2<String, String>, Long> reduce(Tuple2<Tuple2<String, String>, Long> tuple1, Tuple2<Tuple2<String, String>, Long> tuple2) throws Exception {
//                return Tuple2.of(tuple1.f0, tuple1.f1 + tuple2.f1);
//            }
//        }).map(new MapFunction<Tuple2<Tuple2<String, String>, Long>, Statics>() {
//            @Override
//            public Statics map(Tuple2<Tuple2<String, String>, Long> tupleIn) throws Exception {
//                return new Statics(tupleIn.f0.f0, tupleIn.f0.f1, tupleIn.f1);
//            }
//        });

        SingleOutputStreamOperator<Statics> resultDs = scanProductInfoDataStream.map(new MapFunction<ScanProductInfo, Tuple2<String, Long>>() {
            @Override
            public Tuple2<String, Long> map(ScanProductInfo scanProductInfo) throws Exception {
                String productType = scanProductInfo.getProductType();
                String brand = scanProductInfo.getBrand();
                return Tuple2.of("brandLikeOf" + productType+"#"+brand, 1L);
            }
        }).keyBy(e -> e.f0).window(TumblingProcessingTimeWindows.of(Time.days(1))).trigger(ContinuousProcessingTimeTrigger.of(Time.seconds(10))).reduce(new ReduceFunction<Tuple2<String, Long>>() {
            @Override
            public Tuple2<String, Long> reduce(Tuple2<String, Long> t1, Tuple2<String, Long> t2) throws Exception {
                return Tuple2.of(t1.f0, t1.f1+t2.f1);
            }
        }).map(new MapFunction<Tuple2<String, Long>, Statics>() {
            @Override
            public Statics map(Tuple2<String, Long> tuple) throws Exception {
                String[] staticNameAndDetail = tuple.f0.split("#");
                return new Statics(staticNameAndDetail[0], staticNameAndDetail[1], tuple.f1);
            }
        });

        resultDs.addSink(new StaticsSink());

        try {
            env.execute("useType analysis");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  该需求实现设置了1天的滚动窗口且设置了每10s触发一次计算的触发器。

  • 各类产品(包类、服装类、鞋类)近一分钟浏览次数统计,每10s统计一次(超喜欢的需求)
package cn.edu.neu.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author 32098
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ProductTypeScanCountStatic {
    private String productType;
    private String dealtTime;
    private Long scanCount;
}

package cn.edu.neu.task.windowTask;

import cn.edu.neu.bean.ProductTypeScanCountStatic;
import cn.edu.neu.bean.ScanProductInfo;
import cn.edu.neu.sink.ProTypeScanStaticSink;
import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.java.tuple.Tuple4;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;

import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.Date;
import java.util.Properties;

import static org.apache.flink.table.api.Expressions.$;

/**
 * @author 32098
 *
 * 各类产品近一分钟浏览次数统计,每10s统计一次;
 */
public class ScanProductNearlyMinuteCountTask {
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public static class TimeProductTypeCount{
        private Long eventTime;
        private String dealtTime;
        private String productType;
        private long scanCount;
    }

    /**
     *
     * 时间处理逻辑:时间戳->HH:mm:ss->HH:mm:ss',ss->ss' 的处理逻辑如下:
     *
     *         9s(1-10) => 10s
     *         13s(11-20) => 20s
     *         24s(21-30) => 30s
     *         32s(31-40) => 40s
     *         48s(41-50) => 50s
     *         56s(51-60) => 60s(0)
     *         (s / 10 (整除) + 1)*10 : (56/10+1)=60
     *
     * @return 处理好的时间,例子=>12:12:12->12:12:20
     */
    private static String timeProcess(long ts){
        String time = new SimpleDateFormat("HH:mm:ss").format(new Date(ts));
        String[] hms = time.split(":");
        int s = (Integer.parseInt(hms[2])/10+1)*10;
        int m = Integer.parseInt(hms[1]);
        int h = Integer.parseInt(hms[0]);
        if(s == 60){
            m = m + 1;
            s = 0;
            if(m == 60){
                h = h + 1;
                if(h == 24){
                    h = 0;
                }
            }
        }
        String hStr, mStr, sStr;
        if(h < 10){
            hStr = "0" + h;
        }else{
            hStr = String.valueOf(h);
        }
        if(m < 10){
            mStr = "0" + m;
        }else{
            mStr = String.valueOf(m);
        }
        if(s == 0){
            sStr = "00";
        }else{
            sStr = String.valueOf(s);
        }
        return hStr+":"+mStr+":"+sStr;
    }

    public static void main(String[] args) {
        Properties pros = new Properties();
        pros.setProperty("bootstrap.servers", "master:9092");
        pros.setProperty("group.id", "flink");
        pros.setProperty("auto.offset.reset","latest");
        pros.setProperty("flink.partition-discovery.interval-millis","5000");
        pros.setProperty("enable.auto.commit", "true");
        pros.setProperty("auto.commit.interval.ms", "2000");

        FlinkKafkaConsumer<String> kafkaSource = new FlinkKafkaConsumer<String>(
                "flink_kafka",
                new SimpleStringSchema(),
                pros
        );
        kafkaSource.setStartFromLatest();

        // 1. env
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);
        StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);

        // 2. source
        DataStreamSource<String> kafkaDataStream = env.addSource(kafkaSource);

        // 3. transformation
        // to java object
        SingleOutputStreamOperator<ScanProductInfo> scanProductInfoDataStream = kafkaDataStream.map(new MapFunction<String, ScanProductInfo>() {
            @Override
            public ScanProductInfo map(String s) throws Exception {
                return JSON.parseObject(s, ScanProductInfo.class);
            }
        });

        //
        SingleOutputStreamOperator<ScanProductInfo> wateredProductScanDs = scanProductInfoDataStream.assignTimestampsAndWatermarks(
                WatermarkStrategy.<ScanProductInfo>forBoundedOutOfOrderness(Duration.ofSeconds(3)).withTimestampAssigner((scanProductInfo, timeStamp) -> scanProductInfo.getBeginScanTime())
        );

        //
        SingleOutputStreamOperator<TimeProductTypeCount> dealtProductScanDs = wateredProductScanDs.map(new MapFunction<ScanProductInfo, Tuple4<Long, String, String, Long>>() {
            @Override
            public Tuple4<Long, String, String, Long> map(ScanProductInfo scanProductInfo) throws Exception {
                Long eventTime = scanProductInfo.getBeginScanTime();
                String timeDealt = timeProcess(scanProductInfo.getBeginScanTime());
                String productType = scanProductInfo.getProductType();
                return Tuple4.of(eventTime, timeDealt, productType, 1L);
            }
        }).map(new MapFunction<Tuple4<Long, String, String, Long>, TimeProductTypeCount>() {
            @Override
            public TimeProductTypeCount map(Tuple4<Long, String, String, Long> inTuple) throws Exception {
                return new TimeProductTypeCount(inTuple.f0, inTuple.f1, inTuple.f2, inTuple.f3);
            }
        });

        // Cannot apply '$HOP' to arguments of type '$HOP(<BIGINT>, <INTERVAL SECOND>, <INTERVAL SECOND>)'. Supported form(s): '$HOP(<DATETIME>, <DATETIME_INTERVAL>, <DATETIME_INTERVAL>)'
        tEnv.createTemporaryView("temp", dealtProductScanDs, $("eventTime").rowtime(), $("dealtTime"), $("productType"), $("scanCount"));

        Table queryResultTable = tEnv.sqlQuery(
                "SELECT productType, dealtTime, count(scanCount) as scanCount FROM temp GROUP BY productType, dealtTime, HOP(eventTime, interval '10' SECOND, interval '60' SECOND)"
        );

        DataStream<ProductTypeScanCountStatic> resultDs = tEnv.toRetractStream(queryResultTable, ProductTypeScanCountStatic.class).filter(e->e.f0).map(e->e.f1);

        resultDs.addSink(new ProTypeScanStaticSink());

        try {
            env.execute();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  该需求实现设置了开始浏览时间为事件时间且设置了3s的水位线。除此之外,采用了flink的table\sql api实现滑动窗口,即以60s为滑动窗口大小、10s为滑动窗口步长来每10s统计近一分钟各商品浏览次数的需求。

  • 群体用户画像之当日实时终端偏好
package cn.edu.neu.task.windowTask;

import cn.edu.neu.bean.ScanProductInfo;
import cn.edu.neu.bean.Statics;
import cn.edu.neu.sink.StaticsSink;
import com.alibaba.fastjson.JSON;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.ReduceFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.SlidingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.triggers.ContinuousProcessingTimeTrigger;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;

import java.util.Properties;

/**
 *
 * @author 32098
 *
 * 群体用户画像之实时终端偏好
 */
public class UseTypeTask {
    public static void main(String[] args) {
        Properties pros = new Properties();
        pros.setProperty("bootstrap.servers", "master:9092");
        pros.setProperty("group.id", "flink");
        pros.setProperty("auto.offset.reset","latest");
        pros.setProperty("flink.partition-discovery.interval-millis","5000");
        pros.setProperty("enable.auto.commit", "true");
        pros.setProperty("auto.commit.interval.ms", "2000");

        FlinkKafkaConsumer<String> kafkaSource = new FlinkKafkaConsumer<String>(
                "flink_kafka",
                new SimpleStringSchema(),
                pros
        );
        kafkaSource.setStartFromLatest();

        // 1. env
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        // 2. source
        DataStreamSource<String> kafkaDataStream = env.addSource(kafkaSource);

        // 3. transformation
        // to java object
        SingleOutputStreamOperator<ScanProductInfo> scanProductInfoDataStream = kafkaDataStream.map(new MapFunction<String, ScanProductInfo>() {
            @Override
            public ScanProductInfo map(String s) throws Exception {
                return JSON.parseObject(s, ScanProductInfo.class);
            }
        });

        SingleOutputStreamOperator<Statics> resultDs = scanProductInfoDataStream.map(new MapFunction<ScanProductInfo, Tuple2<String, Long>>() {
            @Override
            public Tuple2<String, Long> map(ScanProductInfo scanProductInfo) throws Exception {
                String useType = scanProductInfo.getUseType();
                return Tuple2.of(useType, 1L);
            }
        }).keyBy(e -> e.f0).window(TumblingProcessingTimeWindows.of(Time.days(1))).trigger(ContinuousProcessingTimeTrigger.of(Time.seconds(10))).reduce(new ReduceFunction<Tuple2<String, Long>>() {
            @Override
            public Tuple2<String, Long> reduce(Tuple2<String, Long> tA, Tuple2<String, Long> tB) throws Exception {
                return Tuple2.of(tA.f0, tA.f1+tB.f1);
            }
        }).map(new MapFunction<Tuple2<String, Long>, Statics>() {
            @Override
            public Statics map(Tuple2<String, Long> tupleIn) throws Exception {
                return new Statics("useType", tupleIn.f0, tupleIn.f1);
            }
        });

        resultDs.addSink(new StaticsSink());

        try {
            env.execute("useType analysis");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  该需求实现同样设置了1天的滚动窗口且设置了每10s触发一次计算的触发器。


附:

  • 上述需求实现涉及的Sink类:ProTypeScanStaticSink
package cn.edu.neu.sink;

import cn.edu.neu.bean.ProductTypeScanCountStatic;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat;

/**
 * @author 32098
 */
public class ProTypeScanStaticSink extends RichSinkFunction<ProductTypeScanCountStatic> {
    private long lastInvokeTime = 0;
    private SimpleDateFormat dateFormat = null;

    private Connection conn = null;
    private PreparedStatement ps = null;

    @Override
    public void open(Configuration parameters) throws Exception {
        conn = DriverManager.getConnection("jdbc:mysql://master:3306/user_portrait", "root", "Hive@2020");
        String sql = "";

        sql = "insert into product_type_scan_count(product_type, dealt_time, scan_count) values (?,?,?) on duplicate key update scan_count=?";

        ps = conn.prepareStatement(sql);

        dateFormat = new SimpleDateFormat("ss");
        lastInvokeTime = System.currentTimeMillis();
    }

    @Override
    public void invoke(ProductTypeScanCountStatic value, Context context) throws Exception {
        long invokeTime = System.currentTimeMillis();
        if(Integer.parseInt(dateFormat.format(invokeTime)) - Integer.parseInt(dateFormat.format(lastInvokeTime))>5){
            String sqlDelete = "delete from product_type_scan_count";
            PreparedStatement psDelete = conn.prepareStatement(sqlDelete);
            psDelete.executeUpdate();
        }
        ps.setString(1, value.getProductType());
        ps.setString(2, value.getDealtTime());
        ps.setLong(3, value.getScanCount());
        ps.setLong(4, value.getScanCount());
        ps.executeUpdate();
        lastInvokeTime = System.currentTimeMillis();
    }

    @Override
    public void close() throws Exception {
        if (conn != null) {
            conn.close();
        }
        if (ps != null) {
            ps.close();
        }
    }
}

  • Mysql数据库表:
    在这里插入图片描述
CREATE TABLE `user_portrait`.`product_type_scan_count`  (
  `product_type` varchar(12) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `dealt_time` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `scan_count` bigint(12) NULL DEFAULT NULL,
  PRIMARY KEY (`product_type`, `dealt_time`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

下文链接:基于Flink的个人装扮商城群体用户画像与数据实时统计系统(六)-需求集C实现
上文链接:基于Flink的个人装扮商城群体用户画像与数据实时统计系统(四)-需求集A实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
用户画像,作为一种勾画目标用户、联系用户诉求与设计方向的有效工具,用户画像在各领域得到了广泛的应用。用户画像最初是在电商领域得到应用的,在大数据时代背景下,用户信息充斥在网络中,将用户的每个具体信息抽象成标签,利用这些标签将用户形象具体化,从而为用户提供有针对性的服务。还记得年底收到的支付宝年度消费账单吗?帮助客户回顾一年的消费细节,包括消费能力、消费去向、信用额度等等,再根据每位客户的消费习惯,量身定制商品推荐列表……这一活动,将数据这个量化的词以形象生动的表现手法推到了大众面前。这就是用户画像在电商领域的一个应用,随着我国电子商务的高速发展,越来越多的人注意到数据信息对于电商市场的推动作用。基于数据分析的精准营销方式,可以最大限度的挖掘并留住潜在客户,数据统计与分析为电商市场带来的突破不可估量。在大数据时代,一切皆可“量化”,看似普通的小小数字背后,蕴藏着无限商机,也正在被越来越多的企业所洞悉。如何从大数据中挖掘商机?建立用户画像和精准化分析是关键。什么是用户画像呢?用户画像是根据市场研究和数据,创建的理想中客户虚构的表示。创建用户画像,这将有助于理解现实生活中的目标受众。企业创建的人物角色画像,具体到针对他们的目标和需求,并解决他们的问题,同时,这将帮助企业更加直观的转化客户。用户画像最重要的一个步骤就是对用户标签化,我们要明确要分析用户的各种维度,才能确定如何对用户进行画像。用户画像建立步骤首先,基础数据,电商领域大致分为行为数据、内容偏好数据、交易数据,如浏览量、访问时长、家具偏好、回头率等等。而金融领域又有贷款信息,信用卡,各种征信信息等等。然后,当我们对用户画像所需要的基础数据完毕后,需要对这些资料进行分析和加工,提炼关键要素,构建可视化模型。对收到的数据进行行为建模,抽象出用户的标签。电商领域可能是把用户的基本属性、购买能力、行为特征、兴趣爱好、心理特征、社交网络大致的标签化,而金融风控领域则是更关注用户的基本信息,风险信息,财务信息等等。随后,要利用大数据的整体架构对标签化的过程进行开发实现,对数据进行加工,将标签管理化。同时将标签计算的结果进行计算。这个过程中需要依靠Hive,Hbase等大数据技术,为了提高数据实时性,还要用到Flink,Kafka等实时计算技术。最后,也是最关键的一步,要将我们的计算结果,数据,接口等等,形成服务。比如,图表展示,可视化展示。基于Flink+Alink构建全端亿级实时用户画像系统课程,将带领大家一步一步实现一个强大的实时用户画像系统,该系统以热门的互联网电商实际业务应用场景为案例讲解,具体包含:标签管理(支持动态标签扩展,动态标签指标)、用户预测、用户群体画像、用户行为画像、用户中心、几大内容。本课程采用全新的大数据技术栈:Flink+Alink,让你体验到全新技术栈的强大,感受时代变化的气息,通过学习完本课程可以节省你摸索的时间,节省企业成本,提高企业开发效率。本课程包含的技术: 开发工具为:IDEA、WebStorm Flink1.13.0Alink1.5.0 ClickHouseDolphinSchedulerHadoopHbaseKafkaZookeeper SpringBoot2.0.8.RELEASE SpringCloud Finchley.SR2BinlogCanal MySQL MybatisVue.js、Nodejs、ElementUI 课程亮点: 1.与企业接轨、真实工业界产品2.标签化管理模块功能,支持动态标签扩展3.动态标签指标分析和维护4.Alink算法技术框架 5.大数据热门技术Flink新版本 6.主流微服务后端系统 7.数据实时同步解决方案 8.涵盖主流前端技术VUE+NodeJS+ElementUI 9.成SpringCloud实现统一整合方案 10.互联网大数据企业热门技术栈 11.支持海量数据实时画像 12.支持全端实时画像 13.全程代码实操,提供全部代码和资料 14.提供答疑和提供企业技术方案咨询 
用户画像作为大数据的根基,它抽象出一个用户的信息全貌,为进一步精准、快速地分析用户行为习惯、消费习惯等重要信息,提供了足够的数据基础,奠定了大数据时代的基石。 用户画像,即用户信息标签化,就是企业通过收与分析消费者社会属性、生活习惯、消费行为等主要信息的数据之后,抽象出一个用户的商业全貌作是企业应用大数据技术的基本方式。用户画像为企业提供了足够的信息基础,能够帮助企业快速找到精准用户群体以及用户需求等更为广泛的反馈信息。 用户画像系统能很好地帮助企业分析用户的行为与消费习惯,可以预测商品的发展的趋势,提高产品质量,同时提高用户满意度。构建一个用户画像,包括数据源端数据数据预处理、行为建模、构建用户画像。有些标签是可以直接获取到的,有些标签需要通过数据挖掘分析到!本套课程会带着你一步一步的实现用户画像案例,掌握了本套课程内容,可以让你感受到Flink+ClickHouse技术架构的强大和大数据应用的广泛性。 在这个数据爆发的时代,像大型电商的数据量达到百亿级别,我们往往无法对海量的明细数据做进一步层次的预聚合,大量的业务数据都是好几亿数据关联,并且我们需要聚合结果能在秒级返回。 包括我们的画像数据,也是有这方便的需求,那怎么才能达到秒级返回呢?ClickHouse正好满足我们的需求,它是非常的强大的。 本课程采用Flink+ClickHouse技术架构实现我们的画像系统,通过学习完本课程可以节省你摸索的时间,节省企业成本,提高企业开发效率。希望本课程对一些企业开发人员和对新技术栈有兴趣的伙伴有所帮助,如对我录制的教程内容有建议请及时交流。项目中采用到的算法包含Logistic Regression、Kmeans、TF-IDF等,Flink暂时支持的算法比较少,对于以上算法,本课程将带大家用Flink实现,并且结合真实场景,学完即用。系统包含所有终端的数据(移动端、PC端、小程序端),支持亿级数据量的分析和查询,并且是实时和近实时的对用户进行画像计算。本课程包含的画像指标包含:概况趋势,基础属性,行为特征,兴趣爱好,风险特征,消费特征,营销敏感度,用户标签信息,用户群里,商品关键字等几大指标模块,每个指标都会带大家实现。课程所涵盖的知识点包括:开发工具为:IDEA FlinkClickhouseHadoopHbaseKafkaCanalbinlogSpringBootSpringCloudHDFSVue.jsNode.jsElemntUIEcharts等等 课程亮点: 1.企业级实战、真实工业界产品 2.ClickHouse高性能列式存储数据库 3.提供原始日志数据进行效果检测 4.Flink join企业级实战演练 5.第四代计算引擎Flink+ClickHouse技术架构6.微服务架构技术SpringBoot+SpringCloud技术架构7.算法处理包含Logistic Regression、Kmeans、TF-IDF等8.数据实时同步落地方案实操9.统计终端的数据(移动端、PC端、小程序端) 10.支撑亿级海量数据用户画像平台11.实时和近实时的对用户进行画像计算12.后端+大数据技术栈+前端可视化13.提供技术落地指导支持 14.课程凝聚讲师多年实战经验,经验直接复制15.掌握全部内容能独立进行大数据用户平台的设计和实操企业一线架构师讲授,代码在老师的指导下企业可以复用,提供企业解决方案。  版权归作者所有,盗版将进行法律维权。 
基于Flink与Groovy实现实时动态规则智能营销与风控系统,主要利用了Flink实时流处理能力和Groovy的动态语言特性。 首先,Flink作为一个开源流处理框架,可以支持大规模数据实时处理和分析。通过Flink的流式计算模型和状态管理机制,我们可以实时地对数据流进行分析和处理。在这个系统中,Flink可以承担数据实时传输和处理任务,从不同的数据源获取数据,并进行实时数据清洗、过滤、转换、聚合等操作。 其次,Groovy作为一种动态编程语言,具有灵活的语法和扩展性。在该系统中,我们可以使用Groovy作为规则引擎的脚本语言,编写并执行各种规则。Groovy的动态性使得我们可以根据实际情况,在系统运行过程中动态地修改、添加、删除规则,从而实现实时的动态规则调整和更新。 基于上述两个技术,我们可以实现实时动态规则智能营销与风控系统。该系统可以在实时数据流中根据预先定义或动态添加的规则,判断数据是否满足某些条件,进而触发相应的营销或风控策略。例如,在营销方面,可以根据用户的行为数据,动态地识别用户的购买意向,并实时推送个性化的营销信息;在风控方面,可以根据实时的交易数据,动态地检测风险事件,实施实时的异常交易监控和风险预警。 综上所述,基于Flink与Groovy的全实时动态规则智能营销与风控系统,可以实现实时数据处理和规则匹配,有效地提升营销效果和风控能力。该系统具有高效性、灵活性和可扩展性,能够适应不断变化的业务需求和规则变动。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值