Flink实时数仓项目—DWD层设计与实现

本文详细介绍了在Flink实时数仓项目中,如何设计和实现DWD层的支付宽表。通过关联支付表和订单宽表,满足对商品支付情况的统计需求。选择了以流的方式接受订单宽表并进行间隔连结的实现思路,避免了额外的负担,并确保了数据的准确性。最后展示了支付实体类、支付宽表实体类的创建以及主程序的代码实现,将结果写入Kafka。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

前面完成了三个功能,最后一个功能是支付宽表,与订单宽表有类似的地方。


一、功能四:支付宽表

1.需求描述

业务数据库中的支付表的粒度是一整条订单,但是这里的需求中有计算某商品的支付情况,因此需要将支付表和订单宽表进行关联,方便后续对某商品的支付情况的统计。

2.实现思路

2.1 思路一

因为在功能三中完成了订单宽表的整合,所以可以选择将订单宽表输出到HBase上,把订单宽表在这里作为一个维度表进行管理,在支付宽表时去查询HBase。

2.2 思路二

第二种方案是以流的方式接受订单宽表,然后进行双流Join进行合并。通常情况下,一个订单的支付时间为15分钟之内,所以这里最好还是使用间隔连结,保证能够跟一个区间内的数据进行匹配。

2.3 思路选择

选择思路二,原因如下:
1)因为本身订单宽表就要输出到Kafka中,又要往HBase里写一份,增加了负担。
2)对于支付表来说,它只需要它15分钟内的订单宽表即可,而HBase中却是保存的所有的数据。
3)使用间隔连结更好写代码。

3.代码实现

3.1 创建支付实体类

创建支付实体类是为了将从Kafka读取出来的支付数据转化为JavaBean对象,然后好进行赋值,代码如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PaymentInfo {

    //支付表id
    Long id;
    //订单表id
    Long order_id;
    //用户表id
    Long user_id;
    //该订单支付总价格
    BigDecimal total_amount;
    String subject;
    //该订单支付的方式
    String payment_type;
    //支付的时间
    String create_time;
    //支付成功时间
    String callback_time;
}

3.2 创建支付宽表实体类

支付宽表实体类是我们最终需要的结果,它里面的字段由支付表的字段加上订单宽表的字段组成。同时,需要由一个构造方法,在接受了支付实体和订单宽表实体后能对字段进行赋值,代码如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PaymentWide {

    //从这里开始——支付表里的字段
    Long payment_id;
    String subject;
    String payment_type;
    String payment_create_time;
    String callback_time;


    //总这里开始——订单宽表的字段
    Long detail_id;
    Long order_id;
    Long sku_id;
    BigDecimal order_price;
    Long sku_num;
    String sku_name;
    Long province_id;
    String order_status;
    Long user_id;
    BigDecimal total_amount;
    BigDecimal activity_reduce_amount;
    BigDecimal coupon_reduce_amount;
    BigDecimal original_total_amount;
    BigDecimal feight_fee;
    BigDecimal split_feight_fee;
    BigDecimal split_activity_amount;
    BigDecimal split_coupon_amount;
    BigDecimal split_total_amount;
    String order_create_time;
    String province_name;
    //查询维表得到
    String province_area_code;
    String province_iso_code;
    String province_3166_2_code;
    Integer user_age;
    //用户信息
    String user_gender;
    Long spu_id;
    //作为维度数据 要关联进来
    Long tm_id;
    Long category3_id;
    String spu_name;
    String tm_name;
    String category3_name;
    public PaymentWide(PaymentInfo paymentInfo, OrderWide orderWide) {
        mergeOrderWide(orderWide);
        mergePaymentInfo(paymentInfo);
    }

    //将传进来的支付表的字段的值进行赋值
    public void mergePaymentInfo(PaymentInfo paymentInfo) {
        if (paymentInfo != null) {
            BeanUtils.copyProperties(this, paymentInfo);
            payment_create_time = paymentInfo.create_time;
            payment_id = paymentInfo.id;
        }
    }

    //将传进来的订单宽表的字段的值进行赋值
    public void mergeOrderWide(OrderWide orderWide) {
        if (orderWide != null) {
            BeanUtils.copyProperties(this, orderWide);
            order_create_time = orderWide.create_time;
        }
    }
}

3.3 主程序

主程序步骤很明确:
1)从Kafka相应主体读取支付表和订单宽表的数据
2)将读取到的数据转化为JavaBean对象,然后提取时间戳生成水位线
3)将两个流进行间隔连结,将匹配的数据生成对应的支付宽表并返回
4)将生成的支付宽表数据写入到Kafka对应主题中
代码如下:

public class PaymentWideApp {

    public static void main(String[] args) throws Exception {
        //1、获取流式执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        //设置检查点和状态后端
        env.setStateBackend(new HashMapStateBackend());
        env.getCheckpointConfig().setCheckpointStorage(new FileSystemCheckpointStorage("hdfs://hadoop102:8020/gmall-flink-20220410/ck"));

        env.enableCheckpointing(5000L);
        env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
        env.getCheckpointConfig().setCheckpointTimeout(10000L);
        env.getCheckpointConfig().setMaxConcurrentCheckpoints(2);
        env.getCheckpointConfig().setMinPauseBetweenCheckpoints(3000L);

        //2、读取Kafka中订单宽表的数据
        String groupId = "payment_wide_group";
        String paymentInfoSourceTopic = "dwd_payment_info";
        String orderWideSourceTopic = "dwm_order_wide";
        String paymentWideSinkTopic = "dwm_payment_wide";
        //读取dwd层支付事实表的数据
        DataStreamSource<String> paymentKafkaDS = env.addSource(MyKafkaUtil.getKafkaSource(paymentInfoSourceTopic, groupId));
        //读取dwm层订单宽表的数据
        DataStreamSource<String> orderWideKafkaDS = env.addSource(MyKafkaUtil.getKafkaSource(orderWideSourceTopic, groupId));

        //3、将String类型数据转化为JavaBean对象类型,然后提取时间戳生成水位线
        SingleOutputStreamOperator<PaymentInfo> paymentInfoDS = paymentKafkaDS.map(data -> JSON.parseObject(data, PaymentInfo.class))
                .assignTimestampsAndWatermarks(WatermarkStrategy.<PaymentInfo>forMonotonousTimestamps()
                        .withTimestampAssigner(new SerializableTimestampAssigner<PaymentInfo>() {
                            @SneakyThrows
                            @Override
                            public long extractTimestamp(PaymentInfo paymentInfo, long l) {
                                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                                return simpleDateFormat.parse(paymentInfo.getCreate_time()).getTime();
                            }
                        }));

        SingleOutputStreamOperator<OrderWide> orderWideDS = orderWideKafkaDS.map(data -> JSON.parseObject(data, OrderWide.class))
                .assignTimestampsAndWatermarks(WatermarkStrategy.<OrderWide>forMonotonousTimestamps()
                        .withTimestampAssigner(new SerializableTimestampAssigner<OrderWide>() {
                            @SneakyThrows
                            @Override
                            public long extractTimestamp(OrderWide orderWide, long l) {
                                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                                return simpleDateFormat.parse(orderWide.getCreate_time()).getTime();
                            }
                        }));

        //4、按order_id分组,然后进行间隔连结
        SingleOutputStreamOperator<PaymentWide> paymentWideDS = paymentInfoDS.keyBy(data -> data.getOrder_id())
                .intervalJoin(orderWideDS.keyBy(data -> data.getOrder_id()))
                .between(Time.minutes(-15), Time.seconds(5))
                .process(new ProcessJoinFunction<PaymentInfo, OrderWide, PaymentWide>() {
                    @Override
                    public void processElement(PaymentInfo paymentInfo, OrderWide orderWide, ProcessJoinFunction<PaymentInfo, OrderWide, PaymentWide>.Context context, Collector<PaymentWide> collector) throws Exception {
                        collector.collect(new PaymentWide(paymentInfo, orderWide));
                    }
                });

        //5、关联完毕,将数据写入Kafka
        paymentWideDS.map(data->JSON.toJSONString(data))
                .addSink(MyKafkaUtil.getKafkaSink(paymentWideSinkTopic));

        //6、启动任务
        env.execute();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值