本文已参与「新人创作礼」活动,一起开启掘金创作之路。
文章目录
一 数据生成模块
1 用户行为目标数据
要收集和分析的数据主要包括页面数据、事件数据、曝光数据、启动数据和错误数据。
(1)页面数据
页面数据主要记录一个页面的用户访问情况,包括访问时间、停留时间、页面路径等信息。
-
所有页面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("注册"); -
所有页面对象类型
sku_id("商品skuId"), keyword("搜索关键词"), sku_ids("多个商品skuId"), activity_id("活动id"), coupon_id("购物券id"); -
所有来源类型
promotion("商品推广"), recommend("算法推荐商品"), query("查询结果商品"), activity("促销活动");
(2)事件数据
主要记录应用内一个具体操作行为,包括操作类型、操作对象、操作对象描述等信息。
-
所有动作类型
favor_add("添加收藏"), favor_canel("取消收藏"), cart_add("添加购物车"), cart_remove("删除购物车"), cart_add_num("增加购物车商品数量"), cart_minus_num("减少购物车商品数量"), trade_add_address("增加收货地址"), get_coupon("领取优惠券"); 对于下单、支付等业务数据,可从业务数据库获取。 -
所有动作目标
sku_id("商品"), coupon_id("购物券");
(3)曝光数据
主要记录页面所曝光的内容,包括曝光对象,曝光类型等信息。
-
所有曝光类型
promotion("商品推广"), recommend("算法推荐商品"), query("查询结果商品"), activity("促销活动"); -
所有曝光类型对象类型
sku_id("商品skuId"), activity_id("活动id");
(4)启动数据
记录应用的启动信息。
-
所有启动入口类型
icon("图标"), notification("通知"), install("安装后启动");
(5)错误数据
记录应用使用过程中的错误信息,包括错误编号及错误信息。
2 数据埋点
(1)主流埋点方式
埋点从实现的角度来说就是一段代码(一个代码包),将代码包埋到某一个按钮下面,当鼠标点击按钮的时候就会触发代码包的执行,比如可以收集用户的信息,当前时间,点击了什么,将这些信息包装成一条日志,以请求的方式,发送到后台的日志服务器存储起来。
目前主流的埋点方式,有代码埋点(前端/后端)、可视化埋点、全埋点三种。
-
代码埋点:通过调用埋点SDK函数,在需要埋点的业务逻辑功能位置调用接口,上报埋点数据。例如,我们对页面中的某个按钮埋点后,当这个按钮被点击时,可以在这个按钮对应的 OnClick 函数里面调用SDK提供的数据发送接口,来发送数据。
通过前端html代码实现(主流)。
也可以通过后端代码实现,如业务数据存储到数据库的同时也要将当前的业务行为记录到文件中。一般不会用,业务数据在业务数据库中存在,没有必要再存储一份,后端埋点不能够采集到所有的用户行为,比如用户的一些操作不和后台进行交互,那么就无法采集。
-
可视化埋点:只需要研发人员集成采集 SDK,不需要写埋点代码,业务人员就可以通过访问分析平台的“圈选”功能,来“圈”出需要对用户行为进行捕捉的控件,并对该事件进行命名。圈选完毕后,这些配置会同步到各个用户的终端上,由采集 SDK 按照圈选的配置自动进行用户行为数据的采集和发送。
分析整个页面,当页面分析完成会在后台生成与被分析页面相似的页面,如前者有按钮和超链接,后者也会有按钮和超链接,并且在按钮和超链接的前面有一个小圈圈,直接勾选后会生成一个配置,之后将配置下发到每一个页面,就可以完成埋点操作。
-
全埋点:通过在产品中嵌入SDK,前端自动采集页面上的全部用户行为事件,上报埋点数据,相当于做了一个统一的埋点。然后再通过界面配置哪些数据需要在系统里面进行分析。
可视化埋点中页面所有的东西都要,也就是所有的用户行为数据都会采集。
(2)埋点数据日志结构
根据实际情况有不同的日志结构,以下的结构以电商日志为例。
日志结构大致可分为两类,一是普通页面埋点日志,二是启动日志。
-
普通页面日志结构:每条日志包含当前页面的页面信息,所有事件(动作)、所有曝光信息以及错误信息。除此之外,还包含了一系列公共信息(共享给后续的每一个字段去使用,如actions数据),包括设备信息,地理位置,应用信息等,即下边的common字段。
{ "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 --跳入时间戳 } -
启动日志格式:启动日志结构相对简单,主要包含公共信息,启动信息和错误信息。
{ "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 --标识数据生成的时间 }
(3)买点数据上报时机
埋点数据上报时机包括两种方式:
- 方式一,在离开该页面时,上传在这个页面产生的所有数据(页面、事件、曝光、错误等)。优点,批处理,与后台交互少,减少了服务器接收数据压力。缺点,不是特别及时。
- 方式二,每个事件、动作、错误等,产生后,立即发送。优点,响应及时。缺点,对服务器接收数据压力比较大。
3 服务器和JDK准备
(1)服务器准备
(a) 准备工作
#安装额外软件源
sudo yum install -y epel-release
#安装额外依赖
sudo yum install -y psmisc nc net-tools rsync vim lrzsz ntp libzstd openssl-static
#配置主机映射
vim /etc/hosts #添加如下内容
10.84.126.2 hadoop101
10.84.126.8 hadoop102
10.84.126.11 hadoop103
#关闭防火墙
sudo systemctl stop firewalld
sudo systemctl disable firewalld
sudo systemctl status firewalld 【Active: inactive (dead)】
systemctl is-enabled firewalld 【disabled】
#创建新用户
sudo useradd 用户名
sudo passwd 密码
#配置新用户具有root权限
visudo
#找到91行(:set nu显示行号)
Allow root to run any commands anywhere
root ALL=(ALL) ALL
用户名 ALL=(ALL) NOPASSWD:ALL #可以在sudo vim /etc/sudoers中查看
#在opt目录下创建目录
sudo mkdir /opt/module /opt/software
sudo chown hike:hike /opt/module /opt/software
#更改主机名称
vim /etc/hostname ##添加主机名即可
#更改ip地址(适用于虚拟机)
vim /etc/sysconfig/network-scripts/ifcfg-enp0s25
#完成后重启
reboot
(b) 分发脚本
#!/bin/bash
#1.判断参数个数
if [ $# -lt 1 ]
then
echo Not Enough Arguement!
exit;
fi
#2. 遍历集群所有机器
for host in hadoop102 hadoop103
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
© SSH免密登录
原理:前提,B有A的公钥,A想要登录B。首先A需要将自己的公钥发送给B,B收到A发来的公钥后接收到A想要登录B的请求,此时B要确认对方是不是A,于是,B给A发送一些使用A的公钥进行加密的信息,称为X,正确解析X需要A的私钥,之后B将X发送给A,A使用自己的私钥解析X,将解析的结果发送给B,B自己也会进行解析,A与B解析的结果相同时,A可以登陆B。
ssh-keygen -t rsa 三次回车。ll -a 显示隐藏目录,cd .ssh进入 ll,其中id rsa是私钥,id rsa.public是公钥,将公钥拷贝给要登录的服务器。
ssh-copy-id hadoop101,yes,输入密码,此时文件夹中多了一个authorized keys,存储所有经过认证的可以登陆我的公钥,在登录过程中authorized keys和id rsa进行配对比较。
ssh-copy-id hadoop102,ssh-copy-id hadoop103,同上面操作。此时,101可以和102,103进行免密登录,但是103,102不可以和101进行免密登录,将id rsa发送给102,103即可实现免密登录
(2)准备JDK
#查看系统中是否存在JDK
rpm -qa | grep -i java
#准备安装包,解压
tar -zxvf jdk-8u212-linux-x64.tar.gz -C /opt/module/
#创建环境变量文件
cd /etc/profile.d/
sudo touch my_env.sh
sudo vim my_env.sh
#添加如下内容
#JAVA_HOME
export JAVA_HOME=/opt/module/jdk1.8.0_212
export PATH=$PATH:$JAVA_HOME/bin
#使环境变量生效
source /etc/profile.d/my_env.sh
#查看是否配置成功
java -version
#向其他机器分发JDK
xsync /opt/module/jdk1.8.0_212/
#分发环境变量
sudo /home/hike/bin/xsync /etc/profile.d/my_env.sh #或者
scp -r /etc/profile.d/my_env.sh root@hadoop102:/etc/profile.d/
scp -r /etc/profile.d/my_env.sh root@hadoop103:/etc/profile.d/
#2 3断开重连或者source
(3)环境变量配置说明
Linux的环境变量可在多个文件中配置,如/etc/profile,/etc/profile.d/*.sh,~/.bashrc,/.bash_profile等。
bash的运行模式可分为login shell和non-login shell。
例如,我们通过终端,输入用户名、密码,登录系统之后,得到就是一个login shell,而当我们执行以下命令ssh hadoop103 command,在hadoop103执行command的就是一个non-login shell。
这两种shell的主要区别在于,它们启动时会加载不同的配置文件,login shell启动时会加载/etc/profile,~/.bash_profile,/.bashrc,non-login shell启动时会加载~/.bashrc。
而在加载~/.bashrc(实际是/.bashrc中加载的/etc/bashrc)或/etc/profile时,都会执行如下代码片段,
#/etc/profile中部分代码
for i in /etc/profile.d/*.sh /etc/profile.d/sh.local ; do
if [ -r "$i" ]; then
if [ "${-#*i}" != "$-" ]; then
. "$i"
else
. "$i" >/dev/null
fi
fi
done
#/etc/bashrc中部分代码
for i in /etc/profile.d/*.sh; do
if [ -r "$i" ]; then
if [ "$PS1" ]; then
. "$i"
else
. "$i" >/dev/null
fi
fi
done
因此不管是login shell还是non-login shell,启动时都会加载/etc/profile.d/*.sh中的环境变量。
4 模拟数据
(1)使用说明
#将application.yml、gmall2020-mock-log-2021-01-22.jar、path.json、logback.xml上传到hadoop102的/opt/module/applog目录下
mkdir /opt/module/applog
#gmall2020-mock-log-2021-01-22.jar为在模拟数据时需要执行的代码
#application.yml是JavaEE中的框架SpringBoot的配置文件
#logback.xml是打印日志时日志的配置文件
#path.json是模拟用户的访问轨迹
(2)生成数据
#需要在jar包所在路径执行
java -jar gmall2020-mock-log-2021-01-22.jar
(3)集群日志生成脚本
将以上步骤编写成脚本,其中101,102配置为存储日志的服务器
#!/bin/bash
for i in hadoop101 hadoop102
do
ssh $i 'cd /opt/module/applog ; java -jar gmall2020-mock-log-2021-01-22.jar 1>/dev/null 2>&1 &'
done
#在~/bin目录下创建log文件
cd ~/bin
vim log
#复制以上代码,更改文件权限
chmod u+x log
#分发日志目录
scp -r applog/ hadoop102:/opt/module/
#执行脚本生成日志
log