• 离线数仓 (三) --------- 用户行为数据生成模块



    一、目标数据

    我们要收集和分析的数据主要包括页面数据、事件数据、曝光数据、启动数据和错误数据

    1. 页面

    页面数据主要记录一个页面的用户访问情况,包括访问时间、停留时间、页面路径等信息。

    在这里插入图片描述

    字段名称字段描述
    page_id页面 id :home(“首页”),category(“分类页”),discovery(“发现页”),top_n(“热门排行”),favor(“收藏页”),search(“搜索页”),good_list(“商品列表页”),good_detail(“商品详情”),good_spec(“商品规格”),comment(“评价”),comment_done(“评价完成”),comment_list(“评价列表”),cart(“购物车”),trade(“下单结算”),payment(“支付页面”),payment_done(“支付完成”),orders_all(“全部订单”),orders_unpaid(“订单待支付”),,orders_undelivered(“订单待发货”),orders_unreceipted(“订单待收货”),orders_wait_comment(“订单待评价”),mine(“我的”),activity(“活动”),login(“登录”),register(“注册”)
    last_page_id上页id
    page_item_type页面对象类型: sku_id(“商品skuId”),keyword(“搜索关键词”),sku_ids(“多个商品skuId”),activity_id(“活动id”),coupon_id(“购物券id”);
    page_item页面对象id
    sourceType页面来源类型: promotion(“商品推广”),recommend(“算法推荐商品”),query(“查询结果商品”), activity(“促销活动”)
    during_time停留时间(毫秒)
    ts跳入时间

    2. 事件

    事件数据主要记录应用内一个具体操作行为,包括操作类型、操作对象、操作对象描述等信息。

    在这里插入图片描述

    字段名称字段描述
    action_id动作id: favor_add(“添加收藏”),favor_canel(“取消收藏”),cart_add(“添加购物车”),cart_remove(“删除购物车”),cart_add_num(“增加购物车商品数量”),cart_minus_num(“减少购物车商品数量”),trade_add_address(“增加收货地址”),get_coupon(“领取优惠券”),注:对于下单、支付等业务数据,可从业务数据库获取。
    item_type动作目标类型: sku_id(“商品”),coupon_id(“购物券”)
    item动作目标id
    ts动作时间

    3. 曝光

    曝光数据主要记录页面所曝光的内容,包括曝光对象,曝光类型等信息。

    在这里插入图片描述

    字段名称字段描述
    displayType曝光类型 :promotion(“商品推广”),recommend(“算法推荐商品”),query(“查询结果商品”),activity(“促销活动”);
    item_type曝光对象类型 : sku_id(“商品skuId”),activity_id(“活动id”)、
    item曝光对象id
    order曝光顺序

    4. 启动

    启动数据记录应用的启动信息。

    在这里插入图片描述

    字段名称字段描述
    entry启动入口 : icon(“图标”),notification(“通知”),install(“安装后启动”);
    loading_time启动加载时间
    open_ad_id开屏广告id
    open_ad_ms广告播放时间
    open_ad_skip_ms用户跳过广告时间
    ts启动时间

    5. 错误

    错误数据记录应用使用过程中的错误信息,包括错误编号及错误信息。

    字段名称字段描述
    error_code错误码
    msg错误信息

    二、数据埋点

    1. 主流埋点方式 (了解)

    目前主流的埋点方式,有代码埋点 (前端/后端)、可视化埋点、全埋点三种。

    代码埋点是通过调用埋点SDK函数,在需要埋点的业务逻辑功能位置调用接口,上报埋点数据。例如,我们对页面中的某个按钮埋点后,当这个按钮被点击时,可以在这个按钮对应的 OnClick 函数里面调用SDK 提供的数据发送接口,来发送数据。

    可视化埋点只需要研发人员集成采集 SDK,不需要写埋点代码,业务人员就可以通过访问分析平台的 “圈选” 功能,来 “圈” 出需要对用户行为进行捕捉的控件,并对该事件进行命名。圈选完毕后,这些配置会同步到各个用户的终端上,由采集 SDK 按照圈选的配置自动进行用户行为数据的采集和发送。

    全埋点是通过在产品中嵌入SDK,前端自动采集页面上的全部用户行为事件,上报埋点数据,相当于做了一个统一的埋点。然后再通过界面配置哪些数据需要在系统里面进行分析。

    2. 埋点数据上报时机

    埋点数据上报时机包括两种方式。

    方式一,在离开该页面时,上传在这个页面产生的所有数据(页面、事件、曝光、错误等)。优点,批处理,减少了服务器接收数据压力。缺点,不是特别及时。

    方式二,每个事件、动作、错误等,产生后,立即发送。优点,响应及时。缺点,对服务器接收数据压力比较大。

    本次项目采用方式一埋点。

    3. 埋点数据日志结构

    我们的日志结构大致可分为两类,一是普通页面埋点日志,二是启动日志。

    普通页面日志结构如下,每条日志包含了,当前页面的页面信息,所有事件 (动作) 、所有曝光信息以及错误信息。除此之外,还包含了一系列公共信息,包括设备信息,地理位置,应用信息等,即下边的common字段。

    A、普通页面埋点日志格式

    {
      "common": {                  -- 公共信息
        "ar": "230000",              -- 地区编码
        "ba": "iPhone",              -- 手机品牌
        "ch": "Appstore",            -- 渠道
        "is_new": "1",--是否首日使用,首次使用的当日,该字段值为1,过了24:00,该字段置为0"md": "iPhone 8",            -- 手机型号
        "mid": "YXfhjAYH6As2z9Iq", -- 设备id
        "os": "iOS 13.2.9",          -- 操作系统
        "uid": "485",                 -- 会员id
        "vc": "v2.1.134"             -- app版本号
      },
     "actions": [                     --动作(事件)  
        {
          "action_id": "favor_add",   --动作id
          "item": "3",                   --目标id
          "item_type": "sku_id",       --目标类型
          "ts": 1585744376605           --动作时间戳
        }
      ],
      "displays": [
        {
          "displayType": "query",        -- 曝光类型
          "item": "3",                     -- 曝光对象id
          "item_type": "sku_id",         -- 曝光对象类型
          "order": 1,                      --出现顺序
          "pos_id": 2                      --曝光位置
        },
        {
          "displayType": "promotion",
          "item": "6",
          "item_type": "sku_id",
          "order": 2, 
          "pos_id": 1
        },
        {
          "displayType": "promotion",
          "item": "9",
          "item_type": "sku_id",
          "order": 3, 
          "pos_id": 3
        },
        {
          "displayType": "recommend",
          "item": "6",
          "item_type": "sku_id",
          "order": 4, 
          "pos_id": 2
        },
        {
          "displayType": "query ",
          "item": "6",
          "item_type": "sku_id",
          "order": 5, 
          "pos_id": 1
        }
      ],
      "page": {                       -- 页面信息
        "during_time": 7648,          -- 持续时间毫秒
        "item": "3",                  -- 目标id
        "item_type": "sku_id",        -- 目标类型
        "last_page_id": "login",      -- 上页类型
        "page_id": "good_detail",     -- 页面ID
        "sourceType": "promotion"     -- 来源类型
      },
      "err":{                         -- 错误
    	"error_code": "1234",         -- 错误码
        "msg": "***********"          -- 错误信息
      },
      "ts": 1585744374423             -- 跳入时间戳
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    B、启动日志格式

    启动日志结构相对简单,主要包含公共信息,启动信息和错误信息。

    {
      "common": {
        "ar": "370000",
        "ba": "Honor",
        "ch": "wandoujia",
        "is_new": "1",
        "md": "Honor 20s",
        "mid": "eQF5boERMJFOujcp",
        "os": "Android 11.0",
        "uid": "76",
        "vc": "v2.1.134"
      },
      "start": {   
        "entry": "icon",           -- icon 手机图标  notice 通知  install 安装后启动
        "loading_time": 18803,     -- 启动加载时间
        "open_ad_id": 7,           -- 广告页ID
        "open_ad_ms": 3449,        -- 广告总共播放时间
        "open_ad_skip_ms": 1989    -- 用户跳过广告时点
      },
      "err":{                      -- 错误
    	"error_code": "1234",      -- 错误码
        "msg": "***********"       -- 错误信息
      },
      "ts": 1585744304000
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    三、服务器和 JDK 准备

    1. 服务器

    在此以虚拟机为例,配置三台虚拟器作为 hadoop 所需服务器集群。。。,详见之前写过的 Hadoop (三) --------- Hadoop 运行环境搭建 (虚拟机) 这篇文章

    2. 编写集群分发脚本 xsync

    A、xsync集群分发脚本

    • 需求:循环复制文件到所有节点的相同目录下

    • 需求分析
      ① rsync 命令原始拷贝:rsync -av /opt/module root@node103:/opt/
      ② 期望脚本:xsync要同步的文件名称
      ③ 说明:在 /home/fancy/bin 这个目录下存放的脚本,/home/fancy 用户可以在系统任何地方直接执行。

    B、脚本实现

    ① 在用户的家目录 /home/fancy 下创建 bin 文件夹

    [fancy@hadoop102 ~]$ mkdir bin
    
    • 1

    ② 在 /home/fancy/bin 目录下创建 xsync 文件,以便全局调用

    [fancy@node101  ~]$ cd /home/fancy/bin
    [fancy@node102  ~]$ vim xsync
    
    • 1
    • 2

    在该文件中编写如下代码

    #!/bin/bash
    #1. 判断参数个数
    if [ $# -lt 1 ]
    then
      echo Not Enough Arguement!
      exit;
    fi
    #2. 遍历集群所有机器
    for host in node101 node102 node103
    do
      echo ====================  $host  ====================
      #3. 遍历所有目录,挨个发送
      for file in $@
      do
        #4 判断文件是否存在
        if [ -e $file ]
        then
          #5. 获取父目录
          pdir=$(cd -P $(dirname $file); pwd)
          #6. 获取当前文件的名称
          fname=$(basename $file)
          ssh $host "mkdir -p $pdir"
          rsync -av $pdir/$fname $host:$pdir
        else
          echo $file does not exists!
        fi
      done
    done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    ③修改脚本xsync具有执行权限

    [fancy@node101 bin]$ chmod +x xsync
    
    • 1

    ④测试脚本

    [fancy@node101 bin]$ xsync xsync
    
    • 1

    3. SSH 无密登录配置

    说明:这里面只配置了 node101、node102 到其他主机的无密登录。因为 node101 未外配置的是 NameNode,node102 配置的是 ResourceManager,都要求对其他节点无密访问。

    A、node101 上生成公钥和私钥

    [fancy@node101 .ssh]$ ssh-keygen -t rsa
    
    • 1

    然后敲(三个回车),就会生成两个文件id_rsa(私钥)、id_rsa.pub(公钥)

    B、将 node101 公钥拷贝到要免密登录的目标机器上

    [fancy@node101  .ssh]$ ssh-copy-id node101
    [fancy@node101  .ssh]$ ssh-copy-id node102
    [fancy@node101  .ssh]$ ssh-copy-id node103
    
    • 1
    • 2
    • 3

    C、node102 上生成公钥和私钥

    [fancy@node102 .ssh]$ ssh-keygen -t rsa
    
    • 1

    然后敲(三个回车),就会生成两个文件id_rsa(私钥)、id_rsa.pub(公钥)

    D、将 node102 公钥拷贝到要免密登录的目标机器上

    [fancy@node102 .ssh]$ ssh-copy-id node101	
    [fancy@node102 .ssh]$ ssh-copy-id node102
    [fancy@node102 .ssh]$ ssh-copy-id node103
    
    • 1
    • 2
    • 3

    4. JDK准备

    A、卸载现有JDK(3台节点)

    [fancy@node101 opt]# sudo rpm -qa | grep -i java | xargs -n1 sudo rpm -e --nodeps
    
    [fancy@node102  opt]# sudo rpm -qa | grep -i java | xargs -n1 sudo rpm -e --nodeps
    
    [fancy@node103  opt]# sudo rpm -qa | grep -i java | xargs -n1 sudo rpm -e --nodeps
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • rpm -qa :表示查询所有已经安装的软件包
    • grep -i :表示过滤时不区分大小写
    • xargs -n1 :表示一次获取上次执行结果的一个值
    • rpm -e --nodeps :表示卸载软件

    B、用 XShell 工具将 JDK 导入到 node101 的 /opt/software 文件夹下面

    在Linux系统下的opt目录中查看软件包是否导入成功

    [fancy@node101 software]# ls /opt/software/
    
    • 1

    看到如下结果:

    jdk-8u212-linux-x64.tar.gz
    
    • 1

    C、解压JDK到/opt/module目录下

    [fancy@node101 software]# tar -zxvf jdk-8u212-linux-x64.tar.gz -C /opt/module/
    
    • 1

    D、配置JDK环境变量

    新建 /etc/profile.d/my_env.sh 文件

    [fancy@node101 module]# sudo vim /etc/profile.d/my_env.sh
    
    • 1

    添加如下内容,然后保存(:wq)退出

    #JAVA_HOME
    export JAVA_HOME=/opt/module/jdk1.8.0_212
    export PATH=$PATH:$JAVA_HOME/bin
    
    • 1
    • 2
    • 3

    让环境变量生效

    [fancy@hadoop102 software]$ source /etc/profile.d/my_env.sh
    
    • 1

    E、测试JDK是否安装成功

    [fancy@node101 module]# java -version
    
    • 1

    如果能看到以下结果,则 Java 正常安装

    java version "1.8.0_212"
    
    • 1

    F、分发JDK

    [fancy@node101 module]$ xsync /opt/module/jdk1.8.0_212/
    
    • 1

    G、分发环境变量配置文件

    [fancy@node101 module]$ sudo /home/fancy/bin/xsync /etc/profile.d/my_env.sh
    
    • 1

    H、分别在 node102、node103 上执行 source

    [fancy@node102 module]$ source /etc/profile.d/my_env.sh
    [fancy@node103 module]$ source /etc/profile.d/my_env.sh
    
    • 1
    • 2

    5. 环境变量配置说明

    Linux 的环境变量可在多个文件中配置,如 /etc/profile/etc/profile.d/*.sh~/.bashrc~/.bash_profile 等,下面说明上述几个文件之间的关系和区别。

    bash的运行模式可分为 login shell 和 non-login shell。

    例如,我们通过终端,输入用户名、密码,登录系统之后,得到就是一个 login shell。而当我们执行以下命令ssh node102 command,在 node102 执行command的就是一个non-login shell。

    在这里插入图片描述
    这两种 shell 的主要区别在于,它们启动时会加载不同的配置文件,login shell 启动时会加载/etc/profile~/.bash_profile~/.bashrc。non-login shell启动时会加载 ~/.bashrc

    而在加载 ~/.bashrc(实际是 ~/.bashrc 中加载的 /etc/bashrc)或 /etc/profile 时,都会执行如下代码片段

    在这里插入图片描述

    因此不管是 login shell 还是 non-login shell,启动时都会加载 /etc/profile.d/*.sh 中的环境变量。

    四、模拟数据

    1. 使用说明

    A、将 application.yml、gmall2020-mock-log-2021-01-22.jar、path.json、logback.xml 上传到 node101 的 /opt/module/applog 目录下

    创建 applog 路径

    [fancy@hadoop102 module]$ mkdir /opt/module/applog
    
    • 1

    上传文件 application.yml 到 /opt/module/applog 目录

    B、配置文件

    application.yml文件:

    可以根据需求生成对应日期的用户行为日志。

    [fancy@hadoop102 applog]$ vim application.yml
    
    • 1

    修改如下内容:

    # 外部配置打开
    # 外部配置打开
    logging.config: "./logback.xml"
    #业务日期  注意:并不是Linux系统生成日志的日期,而是生成数据中的时间
    mock.date: "2020-06-14"
    
    #模拟数据发送模式
    #mock.type: "http"
    #mock.type: "kafka"
    mock.type: "log"
    
    #http模式下,发送的地址
    mock.url: "http://hdp1/applog"
    
    #kafka模式下,发送的地址
    mock:
      kafka-server: "hdp1:9092,hdp2:9092,hdp3:9092"
      kafka-topic: "ODS_BASE_LOG"
    
    #启动次数
    mock.startup.count: 200
    #设备最大值
    mock.max.mid: 500000
    #会员最大值
    mock.max.uid: 100
    #商品最大值
    mock.max.sku-id: 35
    #页面平均访问时间
    mock.page.during-time-ms: 20000
    #错误概率 百分比
    mock.error.rate: 3
    #每条日志发送延迟 ms
    mock.log.sleep: 10
    #商品详情来源  用户查询,商品推广,智能推荐, 促销活动
    mock.detail.source-type-rate: "40:25:15:20"
    #领取购物券概率
    mock.if_get_coupon_rate: 75
    #购物券最大id
    mock.max.coupon-id: 3
    #搜索关键词  
    mock.search.keyword: "图书,小米,iphone11,电视,口红,ps5,苹果手机,小米盒子"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    path.json,该文件用来配置访问路径:

    根据需求,可以灵活配置用户点击路径。

    [
    	{"path":["home","good_list","good_detail","cart","trade","payment"],"rate":20 },
    	{"path":["home","search","good_list","good_detail","login","good_detail","cart","trade","payment"],"rate":40 },
    	{"path":["home","mine","orders_unpaid","trade","payment"],"rate":10 },
    	{"path":["home","mine","orders_unpaid","good_detail","good_spec","comment","trade","payment"],"rate":5 },
    	{"path":["home","mine","orders_unpaid","good_detail","good_spec","comment","home"],"rate":5 },
    	{"path":["home","good_detail"],"rate":10 },
    	{"path":["home"  ],"rate":10 }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    logback配置文件:

    可配置日志生成路径,修改内容如下

    
    <configuration>
        <property name="LOG_HOME" value="/opt/module/applog/log" />
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%msg%npattern>
            encoder>
        appender>
    
        <appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${LOG_HOME}/app.%d{yyyy-MM-dd}.logfileNamePattern>
            rollingPolicy>
            <encoder>
                <pattern>%msg%npattern>
            encoder>
        appender>
    
        
        <logger name="com.atgugu.gmall2020.mock.log.util.LogUtil"
                level="INFO" additivity="false">
            <appender-ref ref="rollingFile" />
            <appender-ref ref="console" />
        logger>
    
        <root level="error"  >
            <appender-ref ref="console" />
        root>
    configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    C、生成日志

    进入到 /opt/module/applog 路径,执行以下命令

    [fancy@node101 applog]$ java -jar gmall2020-mock-log-2021-01-22.jar
    
    • 1

    /opt/module/applog/log 目录下查看生成日志

    [fancy@node101 log]$ ll
    
    • 1

    2. 集群日志生成脚本

    在 node101 的 /home/fancy 目录下创建 bin 目录,这样脚本可以在服务器的任何目录执行。

    [fancy@node101 ~]$ echo $PATH
    /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/fancy/.local/bin:/home/fancy/bin
    
    • 1
    • 2

    A、在 /home/fancy/bin 目录下创建脚本lg.sh

    [fancy@node101 bin]$ vim lg.sh
    
    • 1

    B、在脚本中编写如下内容

    #!/bin/bash
    for i in node101 node102; do
        echo "========== $i =========="
        ssh $i "cd /opt/module/applog/; java -jar gmall2020-mock-log-2021-01-22.jar >/dev/null 2>&1 &"
    done 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注:
    /opt/module/applog/ 为 jar 包及配置文件所在路径
    /dev/null 代表 Linux 的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。

    • 标准输入0:从键盘获得输入 /proc/self/fd/0
    • 标准输出1:输出到屏幕(即控制台) /proc/self/fd/1
    • 错误输出2:输出到屏幕(即控制台)/proc/self/fd/2

    C、修改脚本执行权限

    [fancy@hadoop102 bin]$ chmod u+x lg.sh
    
    • 1

    D、将 jar 包及配置文件上传至 node101 的 /opt/module/applog/ 路径

    E、启动脚本

    [fancy@hadoop102 module]$ lg.sh 
    
    • 1

    F、分别在node101、node102的/opt/module/applog/log目录上查看生成的数据

    [fancy@node101 logs]$ ls
    app.2020-06-14.log
    [fancy@node102 logs]$ ls
    app.2020-06-14.log
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    Java的JDBC编程
    Flink 开发环境搭建
    小程序里面循环使用ref的话获取不到
    光芯片-汽车-自动驾驶-新能源分析
    DevOps2023现状报告|注重文化、以用户为中心是成功的关键
    CEAC之《职业素养》
    可重入函数与不可重入函数介绍
    《精通ChatGPT:从入门到大师的Prompt指南》附录C:专业术语表
    Kafka多生产者消费者自动配置
    css实现内凹圆
  • 原文地址:https://blog.csdn.net/m0_51111980/article/details/127372864