电商数仓(架构设计、用户行为数据生成脚本)

写在前面: 本文是我在学习电商数仓项目的过程中,对相关知识点的记录,总结。

文章目录

  • 电商数仓(架构设计、用户行为数据生成脚本)
    • 1. 数据仓库的概念
    • 2. 项目需求及架构设计
      • 2.1 项目需求
      • 2.2 项目框架
        • 2.2.1 技术选型
        • 2.2.2 系统数据流程设计
        • 2.2.3 框架版本选型
        • 2.2.4 服务器选型及资源规划
    • 3. 数据生成模块
      • 3.1 埋点数据基本格式
      • 3.2 事件日志数据
      • 3.3 数据生成脚本
        • 3.3.1 创建 Maven 工程
        • 3.3.2 导入 Bean
        • 3.3.3 主函数
        • 3.3.4 配置日志打印 Logback
        • 3.3.5 打包

1. 数据仓库的概念

  1. 数据仓库,英文名称为 Data Warehouse ,可简写为 DWDWH。数据仓库,是为企业所有级别的决策制定过程,提供所有类型数据支持的战略集合。它是单个数据存储,出于分析性报告和决策支持目的而创建。 为需要业务智能的企业,提供指导业务流程改进、监视时间、成本、质量以及控制。

    摘自百度百科。

2. 项目需求及架构设计

2.1 项目需求

  1. 数据采集平台搭建。
  2. 实现用户行为数仓的分层搭建。
  3. 实现业务数据仓库的分层搭建。
  4. 针对数仓中的数据进行报表分析。

2.2 项目框架

2.2.1 技术选型

  1. 数据采集传输: FlumeKafkaSqoop
  2. 数据存储: HDFSMySQL
  3. 数据计算:HiveTez

2.2.2 系统数据流程设计

2.2.3 框架版本选型

产品 版本
Hadoop 2.7.2
Flume 1.7.0
Kafka 0.11.0.2
Hive 1.2.1
Sqoop 1.4.6
Zookeeper 3.4.10
MySQL 5.6.24
Java 1.8
Azkaban 2.5.0

2.2.4 服务器选型及资源规划

  1. 我这里使用的是三台 Linux 虚拟机(CentOS 6.8) :分别为 Hadoop101Hadoop102Hadoop103

  2. 资源规划:

    服务名称 子服务 Hadoop101 Hadoop102 Hadoop103
    HDFS NameNode
    DataNode
    SecondaryNameNode
    Yarn NodeManager
    ResourceManager
    Zookeeper Zookeeper Server
    Flume(采集数据) Flume
    Kafka Kafka
    Flume(消费Kafka) Flume
    Hive Hive
    MySQL MySQL
    Sqoop Sqoop
    Azkaban AzkabanWebServer
    AzkabanExecutorServer

3. 数据生成模块

3.1 埋点数据基本格式

  1. 公共字段: 基本所有安卓手机都包含的字段。

  2. 业务字段: 埋点上报的字段,有具体的业务类型。

  3. 示例:

    // 字段解释
    {"ap":"xxxxx",//项目数据来源 app pc
    "cm": {  //公共字段"mid": "",  // (String) 设备唯一标识"uid": "",  // (String) 用户标识"vc": "1",  // (String) versionCode,程序版本号"vn": "1.0",  // (String) versionName,程序版本名"l": "zh",  // (String) 系统语言"sr": "",  // (String) 渠道号,应用从哪个渠道来的。"os": "7.1.1",  // (String) Android系统版本"ar": "CN",  // (String) 区域"md": "BBB100-1",  // (String) 手机型号"ba": "blackberry",  // (String) 手机品牌"sv": "V2.2.1",  // (String) sdkVersion"g": "",  // (String) gmail"hw": "1620x1080",  // (String) heightXwidth,屏幕宽高"t": "1506047606608",  // (String) 客户端日志产生时的时间"nw": "WIFI",  // (String) 网络模式"ln": 0,  // (double) lng经度"la": 0  // (double) lat 纬度},
    "et":  [  //事件{"ett": "1506047605364",  //客户端事件产生时间"en": "display",  //事件名称"kv": {  //事件结果,以key-value形式自行定义"goodsid": "236","action": "1","extend1": "1","place": "2","category": "75"}}]
    }// 示例日志
    1540934156385|{"ap": "gmall", "cm": {"uid": "1234", "vc": "2", "vn": "1.0", "la": "EN", "sr": "", "os": "7.1.1", "ar": "CN", "md": "BBB100-1", "ba": "blackberry", "sv": "V2.2.1", "g": "abc@gmail.com", "hw": "1620x1080", "t": "1506047606608", "nw": "WIFI", "ln": 0}, "et": [{"ett": "1506047605364",  //客户端事件产生时间"en": "display",  //事件名称"kv": {  //事件结果,以key-value形式自行定义"goodsid": "236","action": "1","extend1": "1","place": "2","category": "75"}},{"ett": "1552352626835","en": "active_background","kv": {"active_source": "1"}}]}
    }
    

3.2 事件日志数据

事件名称 事件代表字段
商品列表页 loading
商品点击 display
商品详情页 newsdetail
广告 ad
消息通知 notification
用户前台活跃 active_foreground
用户后台活跃 active_background
评论 comment
收藏 favorites
点赞 praise
错误摘要 errorBrief
错误详情 errorDetail
启动 start

3.3 数据生成脚本

3.3.1 创建 Maven 工程

  1. 创建项目log-collector

  2. 创建包:com.guli.appclient

  3. 在包 com.guli.appclient 中,创建一个类 AppMain

  4. 导入依赖:

    <!--版本号统一-->
    <properties><slf4j.version>1.7.20</slf4j.version><logback.version>1.0.7</logback.version>
    </properties><dependencies><!--阿里巴巴开源json解析框架--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.51</version></dependency><!--日志生成框架--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>${logback.version}</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback.version}</version></dependency>
    </dependencies><!--编译打包插件-->
    <build><plugins><plugin><artifactId>maven-compiler-plugin</artifactId><version>2.3.2</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin><plugin><artifactId>maven-assembly-plugin </artifactId><configuration><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs><archive><manifest><!-- 注意:com.guli.appclient.AppMain 要与自己建的全类名一致 --><mainClass>com.guli.appclient.AppMain</mainClass></manifest></archive></configuration><executions><execution><id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin></plugins>
    </build>
    

3.3.2 导入 Bean

  1. 创建包: com.guli.bean

  2. 导入如下 Bean 对象:

    • 公共字段 Bean

      package com.guli.bean;/*** 公共日志*/
      public class AppBase{private String mid; // (String) 设备唯一标识private String uid; // (String) 用户uidprivate String vc;  // (String) versionCode,程序版本号private String vn;  // (String) versionName,程序版本名private String l;   // (String) 系统语言private String sr;  // (String) 渠道号,应用从哪个渠道来的。private String os;  // (String) Android系统版本private String ar;  // (String) 区域private String md;  // (String) 手机型号private String ba;  // (String) 手机品牌private String sv;  // (String) sdkVersionprivate String g;   // (String) gmailprivate String hw;  // (String) heightXwidth,屏幕宽高private String t;   // (String) 客户端日志产生时的时间private String nw;  // (String) 网络模式private String ln;  // (double) lng经度private String la;  // (double) lat 纬度public String getMid() {return mid;}public void setMid(String mid) {this.mid = mid;}public String getUid() {return uid;}public void setUid(String uid) {this.uid = uid;}public String getVc() {return vc;}public void setVc(String vc) {this.vc = vc;}public String getVn() {return vn;}public void setVn(String vn) {this.vn = vn;}public String getL() {return l;}public void setL(String l) {this.l = l;}public String getSr() {return sr;}public void setSr(String sr) {this.sr = sr;}public String getOs() {return os;}public void setOs(String os) {this.os = os;}public String getAr() {return ar;}public void setAr(String ar) {this.ar = ar;}public String getMd() {return md;}public void setMd(String md) {this.md = md;}public String getBa() {return ba;}public void setBa(String ba) {this.ba = ba;}public String getSv() {return sv;}public void setSv(String sv) {this.sv = sv;}public String getG() {return g;}public void setG(String g) {this.g = g;}public String getHw() {return hw;}public void setHw(String hw) {this.hw = hw;}public String getT() {return t;}public void setT(String t) {this.t = t;}public String getNw() {return nw;}public void setNw(String nw) {this.nw = nw;}public String getLn() {return ln;}public void setLn(String ln) {this.ln = ln;}public String getLa() {return la;}public void setLa(String la) {this.la = la;}
      }
      
    • 启动日志 Bean

      package com.guli.bean;/*** 启动日志*/
      public class AppStart extends AppBase {private String entry;//入口: push=1,widget=2,icon=3,notification=4, lockscreen_widget =5private String open_ad_type;//开屏广告类型:  开屏原生广告=1, 开屏插屏广告=2private String action;//状态:成功=1  失败=2private String loading_time;//加载时长:计算下拉开始到接口返回数据的时间,(开始加载报0,加载成功或加载失败才上报时间)private String detail;//失败码(没有则上报空)private String extend1;//失败的message(没有则上报空)private String en;//启动日志类型标记public String getEntry() {return entry;}public void setEntry(String entry) {this.entry = entry;}public String getOpen_ad_type() {return open_ad_type;}public void setOpen_ad_type(String open_ad_type) {this.open_ad_type = open_ad_type;}public String getAction() {return action;}public void setAction(String action) {this.action = action;}public String getLoading_time() {return loading_time;}public void setLoading_time(String loading_time) {this.loading_time = loading_time;}public String getDetail() {return detail;}public void setDetail(String detail) {this.detail = detail;}public String getExtend1() {return extend1;}public void setExtend1(String extend1) {this.extend1 = extend1;}public String getEn() {return en;}public void setEn(String en) {this.en = en;}
      }
      
    • 错误日志 Bean

      package com.guli.bean;/*** 错误日志*/
      public class AppErrorLog {private String errorBrief;    //错误摘要private String errorDetail;   //错误详情public String getErrorBrief() {return errorBrief;}public void setErrorBrief(String errorBrief) {this.errorBrief = errorBrief;}public String getErrorDetail() {return errorDetail;}public void setErrorDetail(String errorDetail) {this.errorDetail = errorDetail;}
      }
      
    • 事件日志 Bean 之商品点击

      package com.guli.bean;/*** 商品点击日志*/
      public class AppDisplay {private String action;//动作:曝光商品=1,点击商品=2,private String goodsid;//商品ID(服务端下发的ID)private String place;//顺序(第几条商品,第一条为0,第二条为1,如此类推)private String extend1;//曝光类型:1 - 首次曝光 2-重复曝光(没有使用)private String category;//分类ID(服务端定义的分类ID)public String getAction() {return action;}public void setAction(String action) {this.action = action;}public String getGoodsid() {return goodsid;}public void setGoodsid(String goodsid) {this.goodsid = goodsid;}public String getPlace() {return place;}public void setPlace(String place) {this.place = place;}public String getExtend1() {return extend1;}public void setExtend1(String extend1) {this.extend1 = extend1;}public String getCategory() {return category;}public void setCategory(String category) {this.category = category;}
      }
      
    • 事件日志 bean 之商品详情页

      package com.guli.bean;/*** 商品详情*/
      public class AppNewsDetail {private String entry;//页面入口来源:应用首页=1、push=2、详情页相关推荐=3private String action;//动作:开始加载=1,加载成功=2(pv),加载失败=3, 退出页面=4private String goodsid;//商品ID(服务端下发的ID)private String showtype;//商品样式:0、无图1、一张大图2、两张图3、三张小图4、一张小图5、一张大图两张小图    来源于详情页相关推荐的商品,上报样式都为0(因为都是左文右图)private String news_staytime;//页面停留时长:从商品开始加载时开始计算,到用户关闭页面所用的时间。若中途用跳转到其它页面了,则暂停计时,待回到详情页时恢复计时。或中途划出的时间超过10分钟,则本次计时作废,不上报本次数据。如未加载成功退出,则报空。private String loading_time;//加载时长:计算页面开始加载到接口返回数据的时间 (开始加载报0,加载成功或加载失败才上报时间)private String type1;//加载失败码:把加载失败状态码报回来(报空为加载成功,没有失败)private String category;//分类ID(服务端定义的分类ID)public String getEntry() {return entry;}public void setEntry(String entry) {this.entry = entry;}public String getAction() {return action;}public void setAction(String action) {this.action = action;}public String getGoodsid() {return goodsid;}public void setGoodsid(String goodsid) {this.goodsid = goodsid;}public String getShowtype() {return showtype;}public void setShowtype(String showtype) {this.showtype = showtype;}public String getNews_staytime() {return news_staytime;}public void setNews_staytime(String news_staytime) {this.news_staytime = news_staytime;}public String getLoading_time() {return loading_time;}public void setLoading_time(String loading_time) {this.loading_time = loading_time;}public String getType1() {return type1;}public void setType1(String type1) {this.type1 = type1;}public String getCategory() {return category;}public void setCategory(String category) {this.category = category;}
      }
      
    • 事件日志 bean 之商品列表页

      package com.guli.bean;/*** 商品列表*/
      public class AppLoading {private String action;//动作:开始加载=1,加载成功=2,加载失败=3private String loading_time;//加载时长:计算下拉开始到接口返回数据的时间,(开始加载报0,加载成功或加载失败才上报时间)private String loading_way;//加载类型:1-读取缓存,2-从接口拉新数据   (加载成功才上报加载类型)private String extend1;//扩展字段 Extend1private String extend2;//扩展字段 Extend2private String type;//加载类型:自动加载=1,用户下拽加载=2,底部加载=3(底部条触发点击底部提示条/点击返回顶部加载)private String type1;//加载失败码:把加载失败状态码报回来(报空为加载成功,没有失败)public String getAction() {return action;}public void setAction(String action) {this.action = action;}public String getLoading_time() {return loading_time;}public void setLoading_time(String loading_time) {this.loading_time = loading_time;}public String getLoading_way() {return loading_way;}public void setLoading_way(String loading_way) {this.loading_way = loading_way;}public String getExtend1() {return extend1;}public void setExtend1(String extend1) {this.extend1 = extend1;}public String getExtend2() {return extend2;}public void setExtend2(String extend2) {this.extend2 = extend2;}public String getType() {return type;}public void setType(String type) {this.type = type;}public String getType1() {return type1;}public void setType1(String type1) {this.type1 = type1;}
      }
      
    • 事件日志 bean 之广告

      package com.guli.bean;/*** 广告*/
      public class AppAd {private String entry;//入口:商品列表页=1  应用首页=2 商品详情页=3private String action;//动作:请求广告=1 取缓存广告=2  广告位展示=3 广告展示=4 广告点击=5private String content;//状态:成功=1  失败=2private String detail;//失败码(没有则上报空)private String source;//广告来源:admob=1 facebook=2  ADX(百度)=3 VK(俄罗斯)=4private String behavior;//用户行为:    主动获取广告=1    被动获取广告=2private String newstype;//Type: 1- 图文 2-图集 3-段子 4-GIF 5-视频 6-调查 7-纯文 8-视频+图文  9-GIF+图文  0-其他private String show_style;//内容样式:无图(纯文字)=6 一张大图=1  三站小图+文=4 一张小图=2 一张大图两张小图+文=3 图集+文 = 5//一张大图+文=11   GIF大图+文=12  视频(大图)+文 = 13//来源于详情页相关推荐的商品,上报样式都为0(因为都是左文右图)public String getEntry() {return entry;}public void setEntry(String entry) {this.entry = entry;}public String getAction() {return action;}public void setAction(String action) {this.action = action;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getDetail() {return detail;}public void setDetail(String detail) {this.detail = detail;}public String getSource() {return source;}public void setSource(String source) {this.source = source;}public String getBehavior() {return behavior;}public void setBehavior(String behavior) {this.behavior = behavior;}public String getNewstype() {return newstype;}public void setNewstype(String newstype) {this.newstype = newstype;}public String getShow_style() {return show_style;}public void setShow_style(String show_style) {this.show_style = show_style;}
      }
      
    • 事件日志 bean 之消息通知

      package com.guli.bean;/*** 消息通知日志*/
      public class AppNotification {private String action;//动作:通知产生=1,通知弹出=2,通知点击=3,常驻通知展示(不重复上报,一天之内只报一次)=4private String type;//通知id:预警通知=1,天气预报(早=2,晚=3),常驻=4private String ap_time;//客户端弹出时间private String content;//备用字段public String getAction() {return action;}public void setAction(String action) {this.action = action;}public String getType() {return type;}public void setType(String type) {this.type = type;}public String getAp_time() {return ap_time;}public void setAp_time(String ap_time) {this.ap_time = ap_time;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}
      }
      
    • 事件日志 bean 之用户前台活跃

      package com.guli.bean;/*** 用户前台活跃*/
      public class AppActive_foreground {private String push_id;//推送的消息的id,如果不是从推送消息打开,传空private String access;//1.push 2.icon 3.其他public String getPush_id() {return push_id;}public void setPush_id(String push_id) {this.push_id = push_id;}public String getAccess() {return access;}public void setAccess(String access) {this.access = access;}
      }
      
    • 事件日志 bean 之用户后台活跃

      package com.guli.bean;/*** 用户后台活跃*/
      public class AppActive_background {private String active_source;//1=upgrade,2=download(下载),3=plugin_upgradepublic String getActive_source() {return active_source;}public void setActive_source(String active_source) {this.active_source = active_source;}
      }
      
    • 事件日志 bean 之用户评论

      package com.guli.bean;/*** 评论*/
      public class AppComment {private int comment_id;//评论表private int userid;//用户idprivate  int p_comment_id;//父级评论id(为0则是一级评论,不为0则是回复)private String content;//评论内容private String addtime;//创建时间private int other_id;//评论的相关idprivate int praise_count;//点赞数量private int reply_count;//回复数量public int getComment_id() {return comment_id;}public void setComment_id(int comment_id) {this.comment_id = comment_id;}public int getUserid() {return userid;}public void setUserid(int userid) {this.userid = userid;}public int getP_comment_id() {return p_comment_id;}public void setP_comment_id(int p_comment_id) {this.p_comment_id = p_comment_id;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getAddtime() {return addtime;}public void setAddtime(String addtime) {this.addtime = addtime;}public int getOther_id() {return other_id;}public void setOther_id(int other_id) {this.other_id = other_id;}public int getPraise_count() {return praise_count;}public void setPraise_count(int praise_count) {this.praise_count = praise_count;}public int getReply_count() {return reply_count;}public void setReply_count(int reply_count) {this.reply_count = reply_count;}
      }
      
    • 事件日志 bean 之用户收藏

      package com.guli.bean;/*** 收藏*/
      public class AppFavorites {private int id;//主键private int course_id;//商品idprivate int userid;//用户IDprivate String add_time;//创建时间public int getId() {return id;}public void setId(int id) {this.id = id;}public int getCourse_id() {return course_id;}public void setCourse_id(int course_id) {this.course_id = course_id;}public int getUserid() {return userid;}public void setUserid(int userid) {this.userid = userid;}public String getAdd_time() {return add_time;}public void setAdd_time(String add_time) {this.add_time = add_time;}
      }
      
    • 事件日志 bean 之用户点赞

      package com.guli.bean;/*** 点赞*/
      public class AppPraise {private int id; //主键idprivate int userid;//用户idprivate int target_id;//点赞的对象idprivate int type;//点赞类型 1问答点赞 2问答评论点赞 3 文章点赞数4 评论点赞private String add_time;//添加时间public int getId() {return id;}public void setId(int id) {this.id = id;}public int getUserid() {return userid;}public void setUserid(int userid) {this.userid = userid;}public int getTarget_id() {return target_id;}public void setTarget_id(int target_id) {this.target_id = target_id;}public int getType() {return type;}public void setType(int type) {this.type = type;}public String getAdd_time() {return add_time;}public void setAdd_time(String add_time) {this.add_time = add_time;}
      }
      

3.3.3 主函数

AppMain 类中添加如下代码:

import java.io.UnsupportedEncodingException;
import java.util.Random;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.guli.bean.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** 日志行为数据模拟*/
public class AppMain {private final static Logger logger = LoggerFactory.getLogger(AppMain.class);private static Random rand = new Random();// 设备idprivate static int s_mid = 0;// 用户idprivate static int s_uid = 0;// 商品idprivate static int s_goodsid = 0;public static void main(String[] args) {// 参数一:控制发送每条的延时时间,默认是0Long delay = args.length > 0 ? Long.parseLong(args[0]) : 0L;// 参数二:循环遍历次数int loop_len = args.length > 1 ? Integer.parseInt(args[1]) : 1000;// 生成数据generateLog(delay, loop_len);}private static void generateLog(Long delay, int loop_len) {for (int i = 0; i < loop_len; i++) {int flag = rand.nextInt(2);switch (flag) {case (0)://应用启动AppStart appStart = generateStart();String jsonString = JSON.toJSONString(appStart);//控制台打印logger.info(jsonString);break;case (1):JSONObject json = new JSONObject();json.put("ap", "app");json.put("cm", generateComFields());JSONArray eventsArray = new JSONArray();// 事件日志// 商品点击,展示if (rand.nextBoolean()) {eventsArray.add(generateDisplay());json.put("et", eventsArray);}// 商品详情页if (rand.nextBoolean()) {eventsArray.add(generateNewsDetail());json.put("et", eventsArray);}// 商品列表页if (rand.nextBoolean()) {eventsArray.add(generateNewList());json.put("et", eventsArray);}// 广告if (rand.nextBoolean()) {eventsArray.add(generateAd());json.put("et", eventsArray);}// 消息通知if (rand.nextBoolean()) {eventsArray.add(generateNotification());json.put("et", eventsArray);}// 用户前台活跃if (rand.nextBoolean()) {eventsArray.add(generatbeforeground());json.put("et", eventsArray);}// 用户后台活跃if (rand.nextBoolean()) {eventsArray.add(generateBackground());json.put("et", eventsArray);}//故障日志if (rand.nextBoolean()) {eventsArray.add(generateError());json.put("et", eventsArray);}// 用户评论if (rand.nextBoolean()) {eventsArray.add(generateComment());json.put("et", eventsArray);}// 用户收藏if (rand.nextBoolean()) {eventsArray.add(generateFavorites());json.put("et", eventsArray);}// 用户点赞if (rand.nextBoolean()) {eventsArray.add(generatePraise());json.put("et", eventsArray);}//时间long millis = System.currentTimeMillis();//控制台打印logger.info(millis + "|" + json.toJSONString());break;}// 延迟try {Thread.sleep(delay);} catch (InterruptedException e) {e.printStackTrace();}}}/*** 公共字段设置*/private static JSONObject generateComFields() {AppBase appBase = new AppBase();//设备idappBase.setMid(s_mid + "");s_mid++;// 用户idappBase.setUid(s_uid + "");s_uid++;// 程序版本号 5,6等appBase.setVc("" + rand.nextInt(20));//程序版本名 v1.1.1appBase.setVn("1." + rand.nextInt(4) + "." + rand.nextInt(10));// 安卓系统版本appBase.setOs("8." + rand.nextInt(3) + "." + rand.nextInt(10));// 语言  es,en,ptint flag = rand.nextInt(3);switch (flag) {case (0):appBase.setL("es");break;case (1):appBase.setL("en");break;case (2):appBase.setL("pt");break;}// 渠道号   从哪个渠道来的appBase.setSr(getRandomChar(1));// 区域flag = rand.nextInt(2);switch (flag) {case 0:appBase.setAr("BR");case 1:appBase.setAr("MX");}// 手机品牌 ba ,手机型号 md,就取2位数字了flag = rand.nextInt(3);switch (flag) {case 0:appBase.setBa("Sumsung");appBase.setMd("sumsung-" + rand.nextInt(20));break;case 1:appBase.setBa("Huawei");appBase.setMd("Huawei-" + rand.nextInt(20));break;case 2:appBase.setBa("HTC");appBase.setMd("HTC-" + rand.nextInt(20));break;}// 嵌入sdk的版本appBase.setSv("V2." + rand.nextInt(10) + "." + rand.nextInt(10));// gmailappBase.setG(getRandomCharAndNumr(8) + "@gmail.com");// 屏幕宽高 hwflag = rand.nextInt(4);switch (flag) {case 0:appBase.setHw("640*960");break;case 1:appBase.setHw("640*1136");break;case 2:appBase.setHw("750*1134");break;case 3:appBase.setHw("1080*1920");break;}// 客户端产生日志时间long millis = System.currentTimeMillis();appBase.setT("" + (millis - rand.nextInt(99999999)));// 手机网络模式 3G,4G,WIFIflag = rand.nextInt(3);switch (flag) {case 0:appBase.setNw("3G");break;case 1:appBase.setNw("4G");break;case 2:appBase.setNw("WIFI");break;}// 拉丁美洲 西经34°46′至西经117°09;北纬32°42′至南纬53°54′// 经度appBase.setLn((-34 - rand.nextInt(83) - rand.nextInt(60) / 10.0) + "");// 纬度appBase.setLa((32 - rand.nextInt(85) - rand.nextInt(60) / 10.0) + "");return (JSONObject) JSON.toJSON(appBase);}/*** 商品展示事件*/private static JSONObject generateDisplay() {AppDisplay appDisplay = new AppDisplay();boolean boolFlag = rand.nextInt(10) < 7;// 动作:曝光商品=1,点击商品=2,if (boolFlag) {appDisplay.setAction("1");} else {appDisplay.setAction("2");}// 商品idString goodsId = s_goodsid + "";s_goodsid++;appDisplay.setGoodsid(goodsId);// 顺序  设置成6条吧int flag = rand.nextInt(6);appDisplay.setPlace("" + flag);// 曝光类型flag = 1 + rand.nextInt(2);appDisplay.setExtend1("" + flag);// 分类flag = 1 + rand.nextInt(100);appDisplay.setCategory("" + flag);JSONObject jsonObject = (JSONObject) JSON.toJSON(appDisplay);return packEventJson("display", jsonObject);}/*** 商品详情页*/private static JSONObject generateNewsDetail() {AppNewsDetail appNewsDetail = new AppNewsDetail();// 页面入口来源int flag = 1 + rand.nextInt(3);appNewsDetail.setEntry(flag + "");// 动作appNewsDetail.setAction("" + (rand.nextInt(4) + 1));// 商品idappNewsDetail.setGoodsid(s_goodsid + "");// 商品来源类型flag = 1 + rand.nextInt(3);appNewsDetail.setShowtype(flag + "");// 商品样式flag = rand.nextInt(6);appNewsDetail.setShowtype("" + flag);// 页面停留时长flag = rand.nextInt(10) * rand.nextInt(7);appNewsDetail.setNews_staytime(flag + "");// 加载时长flag = rand.nextInt(10) * rand.nextInt(7);appNewsDetail.setLoading_time(flag + "");// 加载失败码flag = rand.nextInt(10);switch (flag) {case 1:appNewsDetail.setType1("102");break;case 2:appNewsDetail.setType1("201");break;case 3:appNewsDetail.setType1("325");break;case 4:appNewsDetail.setType1("433");break;case 5:appNewsDetail.setType1("542");break;default:appNewsDetail.setType1("");break;}// 分类flag = 1 + rand.nextInt(100);appNewsDetail.setCategory("" + flag);JSONObject eventJson = (JSONObject) JSON.toJSON(appNewsDetail);return packEventJson("newsdetail", eventJson);}/*** 商品列表*/private static JSONObject generateNewList() {AppLoading appLoading = new AppLoading();// 动作int flag = rand.nextInt(3) + 1;appLoading.setAction(flag + "");// 加载时长flag = rand.nextInt(10) * rand.nextInt(7);appLoading.setLoading_time(flag + "");// 失败码flag = rand.nextInt(10);switch (flag) {case 1:appLoading.setType1("102");break;case 2:appLoading.setType1("201");break;case 3:appLoading.setType1("325");break;case 4:appLoading.setType1("433");break;case 5:appLoading.setType1("542");break;default:appLoading.setType1("");break;}// 页面  加载类型flag = 1 + rand.nextInt(2);appLoading.setLoading_way("" + flag);// 扩展字段1appLoading.setExtend1("");// 扩展字段2appLoading.setExtend2("");// 用户加载类型flag = 1 + rand.nextInt(3);appLoading.setType("" + flag);JSONObject jsonObject = (JSONObject) JSON.toJSON(appLoading);return packEventJson("loading", jsonObject);}/*** 广告相关字段*/private static JSONObject generateAd() {AppAd appAd = new AppAd();// 入口int flag = rand.nextInt(3) + 1;appAd.setEntry(flag + "");// 动作flag = rand.nextInt(5) + 1;appAd.setAction(flag + "");// 状态flag = rand.nextInt(10) > 6 ? 2 : 1;appAd.setContent(flag + "");// 失败码flag = rand.nextInt(10);switch (flag) {case 1:appAd.setDetail("102");break;case 2:appAd.setDetail("201");break;case 3:appAd.setDetail("325");break;case 4:appAd.setDetail("433");break;case 5:appAd.setDetail("542");break;default:appAd.setDetail("");break;}// 广告来源flag = rand.nextInt(4) + 1;appAd.setSource(flag + "");// 用户行为flag = rand.nextInt(2) + 1;appAd.setBehavior(flag + "");// 商品类型flag = rand.nextInt(10);appAd.setNewstype("" + flag);// 展示样式flag = rand.nextInt(6);appAd.setShow_style("" + flag);JSONObject jsonObject = (JSONObject) JSON.toJSON(appAd);return packEventJson("ad", jsonObject);}/*** 启动日志*/private static AppStart generateStart() {AppStart appStart = new AppStart();//设备idappStart.setMid(s_mid + "");s_mid++;// 用户idappStart.setUid(s_uid + "");s_uid++;// 程序版本号 5,6等appStart.setVc("" + rand.nextInt(20));//程序版本名 v1.1.1appStart.setVn("1." + rand.nextInt(4) + "." + rand.nextInt(10));// 安卓系统版本appStart.setOs("8." + rand.nextInt(3) + "." + rand.nextInt(10));//设置日志类型appStart.setEn("start");//    语言  es,en,ptint flag = rand.nextInt(3);switch (flag) {case (0):appStart.setL("es");break;case (1):appStart.setL("en");break;case (2):appStart.setL("pt");break;}// 渠道号   从哪个渠道来的appStart.setSr(getRandomChar(1));// 区域flag = rand.nextInt(2);switch (flag) {case 0:appStart.setAr("BR");case 1:appStart.setAr("MX");}// 手机品牌 ba ,手机型号 md,就取2位数字了flag = rand.nextInt(3);switch (flag) {case 0:appStart.setBa("Sumsung");appStart.setMd("sumsung-" + rand.nextInt(20));break;case 1:appStart.setBa("Huawei");appStart.setMd("Huawei-" + rand.nextInt(20));break;case 2:appStart.setBa("HTC");appStart.setMd("HTC-" + rand.nextInt(20));break;}// 嵌入sdk的版本appStart.setSv("V2." + rand.nextInt(10) + "." + rand.nextInt(10));// gmailappStart.setG(getRandomCharAndNumr(8) + "@gmail.com");// 屏幕宽高 hwflag = rand.nextInt(4);switch (flag) {case 0:appStart.setHw("640*960");break;case 1:appStart.setHw("640*1136");break;case 2:appStart.setHw("750*1134");break;case 3:appStart.setHw("1080*1920");break;}// 客户端产生日志时间long millis = System.currentTimeMillis();appStart.setT("" + (millis - rand.nextInt(99999999)));// 手机网络模式 3G,4G,WIFIflag = rand.nextInt(3);switch (flag) {case 0:appStart.setNw("3G");break;case 1:appStart.setNw("4G");break;case 2:appStart.setNw("WIFI");break;}// 拉丁美洲 西经34°46′至西经117°09;北纬32°42′至南纬53°54′// 经度appStart.setLn((-34 - rand.nextInt(83) - rand.nextInt(60) / 10.0) + "");// 纬度appStart.setLa((32 - rand.nextInt(85) - rand.nextInt(60) / 10.0) + "");// 入口flag = rand.nextInt(5) + 1;appStart.setEntry(flag + "");// 开屏广告类型flag = rand.nextInt(2) + 1;appStart.setOpen_ad_type(flag + "");// 状态flag = rand.nextInt(10) > 8 ? 2 : 1;appStart.setAction(flag + "");// 加载时长appStart.setLoading_time(rand.nextInt(20) + "");// 失败码flag = rand.nextInt(10);switch (flag) {case 1:appStart.setDetail("102");break;case 2:appStart.setDetail("201");break;case 3:appStart.setDetail("325");break;case 4:appStart.setDetail("433");break;case 5:appStart.setDetail("542");break;default:appStart.setDetail("");break;}// 扩展字段appStart.setExtend1("");return appStart;}/*** 消息通知*/private static JSONObject generateNotification() {AppNotification appNotification = new AppNotification();int flag = rand.nextInt(4) + 1;// 动作appNotification.setAction(flag + "");// 通知idflag = rand.nextInt(4) + 1;appNotification.setType(flag + "");// 客户端弹时间appNotification.setAp_time((System.currentTimeMillis() - rand.nextInt(99999999)) + "");// 备用字段appNotification.setContent("");JSONObject jsonObject = (JSONObject) JSON.toJSON(appNotification);return packEventJson("notification", jsonObject);}/*** 前台活跃*/private static JSONObject generatbeforeground() {AppActive_foreground appActive_foreground = new AppActive_foreground();// 推送消息的idint flag = rand.nextInt(2);switch (flag) {case 1:appActive_foreground.setAccess(flag + "");break;default:appActive_foreground.setAccess("");break;}// 1.push 2.icon 3.其他flag = rand.nextInt(3) + 1;appActive_foreground.setPush_id(flag + "");JSONObject jsonObject = (JSONObject) JSON.toJSON(appActive_foreground);return packEventJson("active_foreground", jsonObject);}/*** 后台活跃*/private static JSONObject generateBackground() {AppActive_background appActive_background = new AppActive_background();// 启动源int flag = rand.nextInt(3) + 1;appActive_background.setActive_source(flag + "");JSONObject jsonObject = (JSONObject) JSON.toJSON(appActive_background);return packEventJson("active_background", jsonObject);}/*** 错误日志数据*/private static JSONObject generateError() {AppErrorLog appErrorLog = new AppErrorLog();String[] errorBriefs = {"at cn.lift.dfdf.web.AbstractBaseController.validInbound(AbstractBaseController.java:72)", "at cn.lift.appIn.control.CommandUtil.getInfo(CommandUtil.java:67)"};        //错误摘要String[] errorDetails = {"java.lang.NullPointerException\\n    " + "at cn.lift.appIn.web.AbstractBaseController.validInbound(AbstractBaseController.java:72)\\n " + "at cn.lift.dfdf.web.AbstractBaseController.validInbound", "at cn.lift.dfdfdf.control.CommandUtil.getInfo(CommandUtil.java:67)\\n " + "at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\n" + " at java.lang.reflect.Method.invoke(Method.java:606)\\n"};        //错误详情//错误摘要appErrorLog.setErrorBrief(errorBriefs[rand.nextInt(errorBriefs.length)]);//错误详情appErrorLog.setErrorDetail(errorDetails[rand.nextInt(errorDetails.length)]);JSONObject jsonObject = (JSONObject) JSON.toJSON(appErrorLog);return packEventJson("error", jsonObject);}/*** 为各个事件类型的公共字段(时间、事件类型、Json数据)拼接*/private static JSONObject packEventJson(String eventName, JSONObject jsonObject) {JSONObject eventJson = new JSONObject();eventJson.put("ett", (System.currentTimeMillis() - rand.nextInt(99999999)) + "");eventJson.put("en", eventName);eventJson.put("kv", jsonObject);return eventJson;}/*** 获取随机字母组合** @param length 字符串长度*/private static String getRandomChar(Integer length) {StringBuilder str = new StringBuilder();Random random = new Random();for (int i = 0; i < length; i++) {// 字符串str.append((char) (65 + random.nextInt(26)));// 取得大写字母}return str.toString();}/*** 获取随机字母数字组合** @param length 字符串长度*/private static String getRandomCharAndNumr(Integer length) {StringBuilder str = new StringBuilder();Random random = new Random();for (int i = 0; i < length; i++) {boolean b = random.nextBoolean();if (b) { // 字符串// int choice = random.nextBoolean() ? 65 : 97; 取得65大写字母还是97小写字母str.append((char) (65 + random.nextInt(26)));// 取得大写字母} else { // 数字str.append(String.valueOf(random.nextInt(10)));}}return str.toString();}/*** 收藏*/private static JSONObject generateFavorites() {AppFavorites favorites = new AppFavorites();favorites.setCourse_id(rand.nextInt(10));favorites.setUserid(rand.nextInt(10));favorites.setAdd_time((System.currentTimeMillis() - rand.nextInt(99999999)) + "");JSONObject jsonObject = (JSONObject) JSON.toJSON(favorites);return packEventJson("favorites", jsonObject);}/*** 点赞*/private static JSONObject generatePraise() {AppPraise praise = new AppPraise();praise.setId(rand.nextInt(10));praise.setUserid(rand.nextInt(10));praise.setTarget_id(rand.nextInt(10));praise.setType(rand.nextInt(4) + 1);praise.setAdd_time((System.currentTimeMillis() - rand.nextInt(99999999)) + "");JSONObject jsonObject = (JSONObject) JSON.toJSON(praise);return packEventJson("praise", jsonObject);}/*** 评论*/private static JSONObject generateComment() {AppComment comment = new AppComment();comment.setComment_id(rand.nextInt(10));comment.setUserid(rand.nextInt(10));comment.setP_comment_id(rand.nextInt(5));comment.setContent(getCONTENT());comment.setAddtime((System.currentTimeMillis() - rand.nextInt(99999999)) + "");comment.setOther_id(rand.nextInt(10));comment.setPraise_count(rand.nextInt(1000));comment.setReply_count(rand.nextInt(200));JSONObject jsonObject = (JSONObject) JSON.toJSON(comment);return packEventJson("comment", jsonObject);}/*** 生成单个汉字*/private static char getRandomChar() {String str = "";int hightPos; //int lowPos;Random random = new Random();//随机生成汉子的两个字节hightPos = (176 + Math.abs(random.nextInt(39)));lowPos = (161 + Math.abs(random.nextInt(93)));byte[] b = new byte[2];b[0] = (Integer.valueOf(hightPos)).byteValue();b[1] = (Integer.valueOf(lowPos)).byteValue();try {str = new String(b, "GBK");} catch (UnsupportedEncodingException e) {e.printStackTrace();System.out.println("错误");}return str.charAt(0);}/*** 拼接成多个汉字*/private static String getCONTENT() {StringBuilder str = new StringBuilder();for (int i = 0; i < rand.nextInt(100); i++) {str.append(getRandomChar());}return str.toString();}
}

3.3.4 配置日志打印 Logback

Logback 主要用于在磁盘和控制台打印日志。

Logback 具体使用:

  1. resources 文件夹下创建logback.xml 文件。

  2. logback.xml 文件中填写如下配置

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration debug="false"><!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 --><property name="LOG_HOME" value="/tmp/logs/" /><!-- 控制台输出 --><appender name="STDOUT"class="ch.qos.logback.core.ConsoleAppender"><encoderclass="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern></encoder></appender><!-- 按照每天生成日志文件。存储事件日志 --><appender name="FILE"class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- <File>${LOG_HOME}/app.log</File>设置日志不超过${log.max.size}时的保存路径,注意,如果是web项目会保存到Tomcat的bin目录 下 -->  <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--日志文件输出的文件名 --><FileNamePattern>${LOG_HOME}/app-%d{yyyy-MM-dd}.log</FileNamePattern><!--日志文件保留天数 --><MaxHistory>30</MaxHistory></rollingPolicy><encoderclass="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>%msg%n</pattern></encoder><!--日志文件最大的大小 --><triggeringPolicyclass="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"><MaxFileSize>10MB</MaxFileSize></triggeringPolicy></appender><!--异步打印日志--><appender name ="ASYNC_FILE" class= "ch.qos.logback.classic.AsyncAppender"><!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --><discardingThreshold >0</discardingThreshold><!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --><queueSize>512</queueSize><!-- 添加附加的appender,最多只能添加一个 --><appender-ref ref = "FILE"/></appender><!-- 日志输出级别 --><root level="INFO"><appender-ref ref="STDOUT" /><appender-ref ref="ASYNC_FILE" /><appender-ref ref="error" /></root>
    </configuration>
    

3.3.5 打包

  1. 采用 Maven 对程序打包
  2. 采用带依赖的 jar 包,包含了程序运行需要的所有依赖。

01 电商数仓(架构设计、用户行为数据生成脚本)相关推荐

  1. 数据仓库之电商数仓-- 1、用户行为数据采集

    目录 一.数据仓库概念 二.项目需求及架构设计 2.1 项目需求分析 2.2 项目框架 2.2.1 技术选型 2.2.2 系统数据流程设计 2.2.3 框架版本选型 2.2.4 服务器选型 2.2.5 ...

  2. 电商数仓DWD层用户行为日志解析

    文章目录 前言 一.页面埋点日志.启动日志结构 二.日志解析的流程 2.1 启动日志表解析(包括注意事项) 2.1.1 解析思路 2.1.2 建表语句 2.1.3 数据导入 2.1.4 注意事项 2. ...

  3. 电商数仓笔记2_用户行为采集(数据采集模块)

    电商数仓 一.数据采集模块 1.集群所有进程查看脚本 2.Hadoop安装 (1)项目经验之HDFS存储多目录 (2)集群数据均衡 (3)项目经验之支持LZO压缩配置 (4)项目经验之LZO创建索引 ...

  4. 【尚硅谷】电商数仓V4.0丨大数据数据仓库项目实战【学习记录】第二节

    这里写目录标题 注意 电商数据仓库系统 第6章 数仓搭建-DWD层 6.1.5 动作日志表装载中定义UDTF函数 6.2.6 优惠券领用事实表(累积型快照事实表) 6.2.9 订单事实表(累积型快照事 ...

  5. 数据仓库之电商数仓-- 5、即席查询Kylin

    目录 一.Kylin 1.1 Kylin简介 1.1.1 Kylin定义 1.1.2 Kylin相关术语 1.1.3Kylin架构 1.1.4 Kylin特点 1.2 Kylin安装 1.2.1 Ky ...

  6. 数据仓库之电商数仓-- 4、可视化报表Superset

    目录 一.Superset入门 1.1 Superset概述 1.2 Superset应用场景 二.Superset安装及使用 2.1 安装Python环境 2.1.1 安装Miniconda 2.1 ...

  7. 数据仓库之电商数仓-- 3.4、电商数据仓库系统(ADS层)

    目录 九.数仓搭建-ADS层 9.1 建表说明 9.2 访客主题 9.2.1 访客统计 9.2.2 路径分析 9.3 用户主题 9.3.1 用户统计 9.3.2 用户变动统计 9.3.3 用户行为漏斗 ...

  8. 数据仓库之电商数仓-- 3.3、电商数据仓库系统(DWT层)

    目录 八.数仓搭建-DWT层 8.1 访客主题 8.2 用户主题 8.3 商品主题 8.4 优惠券主题 8.5 活动主题 8.6 地区主题 8.7 DWT层首日数据导入脚本 8.8 DWT层每日数据导 ...

  9. 数据仓库之电商数仓-- 3.2、电商数据仓库系统(DWS层)

    目录 七.数仓搭建-DWS层 7.1 系统函数 7.1.1 nvl函数 7.1.2 日期处理函数 7.1.3 复杂数据类型定义 7.2 DWS层 7.2.1 访客主题 7.2.2 用户主题 7.2.3 ...

最新文章

  1. 排序算法系列:插入排序算法
  2. ffmpeg推流时与服务器断开后的自动重连功能的实现
  3. 【Linux】一步一步学Linux——split命令(59)
  4. 高效管理ASP.NET的JavaScript库
  5. linux查找指定修改时间的文件夹,linux 查找某个日期以后修改过哪些文件 shell脚本...
  6. pip3 安装pycrypto 时报错
  7. 数据推动变革 PMC创新存储方案应对挑战
  8. 【PC工具】更新win10关闭更新工具Windows Update Blocker
  9. Nginx代理高德API(无法地图选点)
  10. emc re 整改 超标_EMC测试及整改对策.ppt
  11. vnc报错 PID file /home/root/.vnc/localhost.pid not readable after start.
  12. 域渗透非约束委派Spooler
  13. 天津市儿童计算机编程课,天津2021少儿编程(少儿机器人编程是什么)
  14. uniapp小程序 触底刷新(onReachBottom)加载分页
  15. MySQL 数据库之 MMM 高可用架构构建
  16. 鲁泰纺织:在行业整合中稳健前行
  17. 虹科分享 | 盘点世界杯有趣小知识!带你感受体育赛事可视化的快乐!
  18. win10彻底关闭电脑的自动更新
  19. 在vscode编辑器里使用leetcode插件刷题
  20. 卷积、自相关函数、功率谱密度

热门文章

  1. 从一道CTF题学习PHP反序列化漏洞
  2. FashionAI服装属性标签识别竞赛
  3. 龙芯2K1000实战开发-时钟设计
  4. 查看android手机arm版本
  5. Python数据攻略-Pandas数据重塑及透视表
  6. HTC G12刷ROM的详细过程
  7. Python爬虫:抓取手机APP数据
  8. 我相信每个人都有选择自己生活方式的权利 —— 写在美术专栏前面
  9. 人生感悟:信仰的真谛
  10. 华为AI框架昇思MindSpore三周年开发者生日会