目录
一、目标数据
我们要收集和分析的数据主要包括页面数据、事件数据、曝光数据、启动数据和错误数据。
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 -- 跳入时间戳
}
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
}
三、服务器和 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
② 在 /home/fancy/bin 目录下创建 xsync 文件,以便全局调用
[fancy@node101 ~]$ cd /home/fancy/bin
[fancy@node102 ~]$ vim xsync
在该文件中编写如下代码
#!/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
③修改脚本xsync具有执行权限
[fancy@node101 bin]$ chmod +x xsync
④测试脚本
[fancy@node101 bin]$ xsync xsync
3. SSH 无密登录配置
说明:这里面只配置了 node101、node102 到其他主机的无密登录。因为 node101 未外配置的是 NameNode,node102 配置的是 ResourceManager,都要求对其他节点无密访问。
A、node101 上生成公钥和私钥
[fancy@node101 .ssh]$ ssh-keygen -t rsa
然后敲(三个回车),就会生成两个文件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
C、node102 上生成公钥和私钥
[fancy@node102 .ssh]$ ssh-keygen -t rsa
然后敲(三个回车),就会生成两个文件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
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
rpm -qa
:表示查询所有已经安装的软件包grep -i
:表示过滤时不区分大小写xargs -n1
:表示一次获取上次执行结果的一个值rpm -e --nodeps
:表示卸载软件
B、用 XShell 工具将 JDK 导入到 node101 的 /opt/software 文件夹下面
在Linux系统下的opt目录中查看软件包是否导入成功
[fancy@node101 software]# ls /opt/software/
看到如下结果:
jdk-8u212-linux-x64.tar.gz
C、解压JDK到/opt/module目录下
[fancy@node101 software]# tar -zxvf jdk-8u212-linux-x64.tar.gz -C /opt/module/
D、配置JDK环境变量
新建 /etc/profile.d/my_env.sh 文件
[fancy@node101 module]# sudo vim /etc/profile.d/my_env.sh
添加如下内容,然后保存(:wq)退出
#JAVA_HOME
export JAVA_HOME=/opt/module/jdk1.8.0_212
export PATH=$PATH:$JAVA_HOME/bin
让环境变量生效
[fancy@hadoop102 software]$ source /etc/profile.d/my_env.sh
E、测试JDK是否安装成功
[fancy@node101 module]# java -version
如果能看到以下结果,则 Java 正常安装
java version "1.8.0_212"
F、分发JDK
[fancy@node101 module]$ xsync /opt/module/jdk1.8.0_212/
G、分发环境变量配置文件
[fancy@node101 module]$ sudo /home/fancy/bin/xsync /etc/profile.d/my_env.sh
H、分别在 node102、node103 上执行 source
[fancy@node102 module]$ source /etc/profile.d/my_env.sh
[fancy@node103 module]$ source /etc/profile.d/my_env.sh
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
上传文件 application.yml 到 /opt/module/applog 目录
B、配置文件
application.yml文件:
可以根据需求生成对应日期的用户行为日志。
[fancy@hadoop102 applog]$ vim application.yml
修改如下内容:
# 外部配置打开
# 外部配置打开
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,苹果手机,小米盒子"
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 }
]
logback配置文件:
可配置日志生成路径,修改内容如下
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_HOME" value="/opt/module/applog/log" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</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}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</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>
C、生成日志
进入到 /opt/module/applog
路径,执行以下命令
[fancy@node101 applog]$ java -jar gmall2020-mock-log-2021-01-22.jar
在 /opt/module/applog/log
目录下查看生成日志
[fancy@node101 log]$ ll
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
A、在 /home/fancy/bin 目录下创建脚本lg.sh
[fancy@node101 bin]$ vim lg.sh
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
注:
① /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
D、将 jar 包及配置文件上传至 node101 的 /opt/module/applog/
路径
E、启动脚本
[fancy@hadoop102 module]$ lg.sh
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