文章目录
一 DWS层与DWM层的设计
1 设计思路
在之前通过分流等手段,把数据分拆成了独立的kafka topic。那么接下来如何处理数据,就要思考一下到底要通过实时计算出哪些指标项。
因为实时计算与离线不同,实时计算的开发和运维成本都是非常高的,要结合实际情况考虑是否有必要像离线数仓一样,建一个大而全的中间层。
如果没有必要大而全,这时候就需要大体规划一下要实时计算出的指标需求了。把这些指标以主题宽表的形式输出就形成了DWS层。
2 需求梳理
当然除以下需求,实际需求还会有更多,这里主要以为可视化大屏为目的进行实时计算的处理。
统计主题 | 需求指标 | 输出方式 | 计算来源 | 来源层级 |
---|---|---|---|---|
访客 | pv | 可视化大屏 | page_log直接可求 | dwd |
uv | 可视化大屏 | 需要用page_log过滤去重 | dwm | |
跳出次数 | 可视化大屏 | 需要通过page_log行为判断 | dwm | |
进入页面数 | 可视化大屏 | 需要识别开始访问标识 | dwd | |
连续访问时长 | 可视化大屏 | page_log直接可求 | dwd | |
商品 | 点击 | 多维分析 | page_log直接可求 | dwd |
曝光 | 多维分析 | page_log直接可求 | dwd | |
收藏 | 多维分析 | 收藏表 | dwd | |
加入购物车 | 多维分析 | 购物车表 | dwd | |
下单 | 可视化大屏 | 订单宽表 | dwm | |
支付 | 多维分析 | 支付宽表 | dwm | |
退款 | 多维分析 | 退款表 | dwd | |
评论 | 多维分析 | 评论表 | dwd | |
地区 | pv | 多维分析 | page_log直接可求 | dwd |
uv | 多维分析 | 需要用page_log过滤去重 | dwm | |
下单 | 可视化大屏 | 订单宽表 | dwm | |
关键词 | 搜索关键词 | 可视化大屏 | 页面访问日志 直接可求 | dwd |
点击商品关键词 | 可视化大屏 | 商品主题下单再次聚合 | dws | |
下单商品关键词 | 可视化大屏 | 商品主题下单再次聚合 | dws |
3 DWS层定位
轻度聚合,因为DWS层要应对很多实时查询,如果是完全的明细,查询的压力是非常大的。
将更多的实时数据以主题的方式组合起来便于管理,同时也能减少维度查询的次数。
二 DWS层-访客主题计算
统计主题 | 需求指标 | 输出方式 | 计算来源 | 来源层级 |
---|---|---|---|---|
访客 | pv | 可视化大屏 | page_log直接可求 | dwd |
uv | 可视化大屏 | 需要用page_log过滤去重 | dwm | |
跳出次数 | 可视化大屏 | 需要通过page_log行为判断 | dwm | |
进入页面数 | 可视化大屏 | 需要识别开始访问标识 | dwd | |
连续访问时长 | 可视化大屏 | page_log直接可求 | dwd |
设计一张DWS层的表其实就两件事:维度和度量(事实数据)
- 度量包括PV、UV、跳出次数、进入页面数(session_count)、连续访问时长
- 维度包括在分析中比较重要的几个字段:渠道、地区、版本、新老用户进行聚合
1 需求分析与思路
- 接收各个明细数据,变为数据流。
- 把数据流合并在一起,成为一个相同格式对象的数据流。
- 对合并的流进行聚合,聚合的时间窗口决定了数据的时效性。
- 把聚合结果写在数据库中。
为了将三条流合并到一起,需要定义一个实体类VisitorStats【渠道、地区、版本、新老用户、PV、UV、跳出次数、进入页面数(session_count)、连续访问时长】
dwd_page_log
new VisitorStats(渠道、地区、版本、新老用户、1L、0L、0L、1L、XXXXL)
dwm_unique_visitor
new VisitorStats(渠道、地区、版本、新老用户、0L、1L、0L、0L、0L)
dwm_user_jump_detail
new VisitorStats(渠道、地区、版本、新老用户、0L、0L、1L、0L、0L)
整体流程如下:
2 功能实现
(1)封装VisitorStatsApp,读取Kafka各个流数据
a 代码
package com.hzy.gmall.realtime.app.dws;
/**
* 访客主题统计dws
*/
public class VisitorStatsApp {
public static void main(String[] args) throws Exception {
// TODO 1 基本环境准备
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(4);
// TODO 2 从kafka中读取数据
// 2.1 声明读取的主题和消费者组
String pageViewSourceTopic = "dwd_page_log";
String uniqueVisitSourceTopic = "dwm_unique_visitor";
String userJumpDetailSourceTopic = "dwm_user_jump_detail";
String groupId = "visitor_stats_app";
// 2.2 获取kafka消费者
FlinkKafkaConsumer<String> pvSource = MyKafkaUtil.getKafkaSource(pageViewSourceTopic, groupId);
FlinkKafkaConsumer<String> uvSource = MyKafkaUtil.getKafkaSource(uniqueVisitSourceTopic, groupId);
FlinkKafkaConsumer<String> ujdSource = MyKafkaUtil.getKafkaSource(userJumpDetailSourceTopic, groupId);
// 2.3 读取数据,封装成流
DataStreamSource<String> pvStrDS = env.addSource(pvSource);
DataStreamSource<String> uvStrDS = env.addSource(uvSource);
DataStreamSource<String> ujdStrDS = env.addSource(ujdSource);
pvStrDS.print("1111");
uvStrDS.print("2222");
ujdStrDS.print("3333");
env.execute();
}
}
b 测试
启动zookeeper,kafka,logger.sh,BaseLogApp、UnionVistorApp、UserJumpDetailAPP、VisitorStatsApp,执行模拟用户行为日志生成脚本,查看是否包含以上三中类型信息。
(2)合并数据流
把数据流合并在一起,成为一个相同格式对象的数据流
合并数据流的核心算子是union。但是union算子,要求所有的数据流结构必须一致。所以union前要调整数据结构。
a 封装主题宽表实体类VisitorStats
package com.hzy.gmall.realtime.beans;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* Desc: 访客统计实体类 包括各个维度和度量
*/
@Data
@AllArgsConstructor
public class VisitorStats {
//统计开始时间
private String stt