大数据学习day39----数据仓库02------1. log4j 2. 父子maven工程(子spring项目的创建)3.项目开发(埋点日志预处理-json数据解析、清洗过滤、数据集成实现、uid回补)
1. log4j(具体见log4j文档)
log4j是一个java系统中用于输出日志信息的工具。log4j可以将日志定义成多种级别:ERROR / WARN / INFO / DEBUG
log4j通过获取到一个logger对象来输出日志:
val logger = Logger.getLogger("logger名称");
logger.info("日志内容")
所拿到的这些logger对象之间是有“父子”关系的,所有logger都是rootLogger的子!
"org.apache" 这个名字的logger是 "org"这个名字的logger的子!
log4j的日志输出格式和目的地,都是可以通过参数配置的;
目的地的控制用Appender输出组件
常用的Appender组件:
log4j.appender.xx=org.apache.log4j.ConsoleAppender
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
格式的控制用LayOut布局组件
log4j.appender.xx.layout=org.apache.log4j.PatternLayout
log4j.appender.xx.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
2. 父子maven工程
(1)创建一个父工程(如平常创建一样),父工程中不写代码,所以最好将src文件夹删除(比如公司新手会将代码误写入该文件夹)
(2)创建子工程
得到如下图
接着如下所示
到此,一个子maven项目dataware即建立成功,子项目的pom文件如下所示
若是子工程中的父工程配置删除后,子工程不认识父工程,但是父工程认识子工程
(3)说明
A. 父工程pom文件中引入公共的依赖和插件(会被子工程pom继承),此处有几处规范
- 依赖定义的管理(不是真正引入依赖) 标签:<dependencyManagement><dependencyManagement>
作用:父项目中某个子项目需要用到某个依赖,这个时候若是在子项目的pom文件中定义这个依赖的版本,当另外一个子项目也要这个依赖时,由于需要统一依赖的版本,这时另外一个子项目中也需要定义相同版本的依赖。这样就比较麻烦,这个时候就可以使用依赖定义的管理(在父工程中定义子项目需要依赖的版本,子项目中就不需要写依赖的版本),如下
父工程pom文件(部分)
<dependencyManagement>
<dependency>
<groupId>ch.hsr</groupId>
<artifactId>geohash</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
</dependencyManagement>
子工程pom文件
- 属性定义(标签:<properties><properties>)
- 依赖排除(标签:<exclusions><exclusions>): 解决jar包的版本冲突
比如下面的spark使用的hadoop版本就出现依赖的冲突
解决办法(排除依赖)
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>${spark.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
</exclusion>
</exclusions>
</dependency>
- 当在idea删除某个项目时,再创建一个同名的项目时,会出错(Idea中记录的东西会冲突)
解决办法:
直接到项目的目录中将idea的相关文件删除掉,如下图所示
spring子项目的创建
3.项目开发(埋点日志预处理-json数据解析、清洗过滤、数据集成实现、uid回补)
3.1 json数据格式如下:
3.2 需求说明
3.2.1 清洗过滤
此处为了记录数据方便,定义一个AppLogBean,该类中定义了两个方法(1.解析json 返回一个case class, 2. 判断一个bean是否有效),并在该类中定义一个case class AppLogBean
AppLogBean代码
package com._51doit.tian.dw.pre import com.alibaba.fastjson.{JSON, JSONObject}
import org.apache.commons.lang3.StringUtils import scala.collection.mutable case class AppLogBean(
eventid :String ,
timestamp :Double ,
event :Map[String,String] ,
uid :String ,
phoneNbr :String ,
sessionId :String ,
imei :String ,
mac :String ,
imsi :String ,
osName :String ,
osVer :String ,
androidId :String ,
resolution :String ,
deviceType :String ,
deviceId :String ,
uuid :String ,
appid :String ,
appVer :String ,
release_ch :String ,
promotion_ch :String ,
longtitude :Double ,
latitude :Double ,
carrier :String ,
netType :String ,
cid_sn :String ,
ip :String,
var province:String = "",
var city:String = "",
var district:String = "",
var dateStr:String = "",
var timeStr:String = ""
) object AppLogBean {
/**
* 解析app埋点json日志,返回一个case class
*/
def parseJson2Bean(line:String): AppLogBean ={
try {
val obj: JSONObject = JSON.parseObject(line)
val eventid: String = obj.getString("eventid")
val timestamp = obj.getString("timestamp").toDouble
val event: JSONObject = obj.getJSONObject("event")
val eventMap: mutable.HashMap[String, String] = new mutable.HashMap[String, String]()
import scala.collection.JavaConversions._
for(ent <- event.entrySet()){
eventMap.put(ent.getKey,ent.getValue.toString)
}
val user = obj.getJSONObject("user")
val uid = user.getString("uid")
val phoneNbr = user.getString("phoneNbr")
val sessionId = user.getString("sessionId") val phone = user.getJSONObject("phone")
val imei = phone.getString("imei")
val mac = phone.getString("mac")
val imsi = phone.getString("imsi")
val osName = phone.getString("osName")
val osVer = phone.getString("osVer")
val androidId = phone.getString("androidId")
val resolution = phone.getString("resolution")
val deviceType = phone.getString("deviceType")
val deviceId = phone.getString("deviceId")
val uuid = phone.getString("uuid") val app = user.getJSONObject("app")
val appid = app.getString("appid")
val appVer = app.getString("appVer")
val release_ch = app.getString("release_ch")
val promotion_ch = app.getString("promotion_ch") val loc = user.getJSONObject("loc")
val longtitude = loc.getDouble("longtitude")
val latitude = loc.getDouble("latitude")
val carrier = loc.getString("carrier")
val netType = loc.getString("netType")
val cid_sn = loc.getString("cid_sn")
val ip = loc.getString("ip") AppLogBean(
eventid ,
timestamp,
eventMap.toMap,
uid ,
phoneNbr ,
sessionId ,
imei ,
mac ,
imsi ,
osName ,
osVer ,
androidId ,
resolution ,
deviceType ,
deviceId ,
uuid ,
appid ,
appVer ,
release_ch ,
promotion_ch ,
longtitude ,
latitude ,
carrier ,
netType ,
cid_sn ,
ip
)
} catch {
case e: Exception => null
case _: Throwable => null
}
} /**
* 判断一条bean是否有效
*/
def isValidBean(bean:AppLogBean): Boolean ={
val uid: String = bean.uid
val imei: String = bean.imei
val uuid: String = bean.uuid
val mac: String = bean.mac
val androidId: String = bean.androidId
val ip: String = bean.ip
// 以上参数不能全为空
var flag1 = StringUtils.isNotBlank((uid + imei + uuid + mac + androidId + ip).replaceAll("null", ""))
val event: Map[String, String] = bean.event
val eventid: String = bean.eventid
val sessionId = bean.sessionId
var flag2 = (event != null) && (StringUtils.isNotBlank(eventid) ) && (StringUtils.isNotBlank(sessionId))
flag1 && flag2
} }
3.2.2 数据解析
此处event数据不用扁平化的原因是,event内的数据类型也不一样
3.2.3 数据集成
3.2.4 数据修正
思路图
注意:此处手机标识 比如imei为空时,作为join on相等的条件时会出错,一定要判断非空,由于sql语句很麻烦(如下),所以开发一个自定义函数,用来判断两个字符串在非空情况下是否相等
每一个手机识别方式都要这样写,很麻烦,以下是自定义的函数
// 开发一个自定义函数,用来判断两个字符串在非空情况下是否相等
val is_equal = (x: String, y: String) => {
if (x != y || StringUtils.isBlank(x) || StringUtils.isBlank(y)) false else true
}
spark.udf.register("is_equal",is_equal)
业务代码
AppEventLogPreprocess
package com._51doit.tian.dw.pre import java.text.SimpleDateFormat import ch.hsr.geohash.GeoHash
import com._51doit.tian.commons.utils.{DictLoadUtil, SparkUtils}
import org.apache.commons.lang3.StringUtils
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession} object AppEventLogPreprocess {
def main(args: Array[String]): Unit = {
Logger.getLogger("org").setLevel(Level.WARN)
val spark: SparkSession = SparkUtils.getSpark(this.getClass.getSimpleName)
import spark.implicits._
// 加载原始日志文件
val ds: Dataset[String] = spark.read.textFile("E:\\javafile\\dataware\\2019-10-29")
// 解析json
val beans: Dataset[AppLogBean] = ds.map(AppLogBean.parseJson2Bean) /**
* 清洗过滤
*/
val validBeans: Dataset[AppLogBean] = beans
.filter(_ != null)
.filter(AppLogBean.isValidBean(_)) /**
* 数据集成
*/
val dictDF: DataFrame = spark.read.parquet("E:/javafile/spark/out11")
val dictMap: collection.Map[String, (String, String, String)] = DictLoadUtil.loadAreaDict(dictDF)
val bc_dict = spark.sparkContext.broadcast(dictMap)
// 然后进行集成
val integrated: Dataset[AppLogBean] = validBeans.mapPartitions(iter => {
// 取广播变量
val dict: collection.Map[String, (String, String, String)] = bc_dict.value iter.map(bean => {
// 处理GPS坐标
val longtitude: Double = bean.longtitude
val latitude: Double = bean.latitude
// 如果经纬度坐标在中国的经纬度范围之内,才去转geohash编码并从字典中查找省市区
if (longtitude > 0 && longtitude < 120 && latitude > 0 && latitude < 70) {
val geo = GeoHash.withCharacterPrecision(latitude, longtitude, 5).toBase32
val area = dict.getOrElse(geo, ("", "", ""))
bean.province = area._1
bean.city = area._2
bean.district = area._3
}
// 处理时间戳
val sdf: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val str: Array[String] = sdf.format(bean.timestamp).split(" ")
bean.dateStr = str(0)
bean.timeStr = str(1)
// 返回集成完成的bean
bean
})
})
// integrated.where("trim(province) != '' ").show(10,false)
/**
* 数据修正
*/
val haveUid = integrated.where("uid is not null and trim(uid) !='' ")
val noUid = integrated.where(" uid is null or trim(uid) ='' ")
import org.apache.spark.sql.functions._
val uids = haveUid
.groupBy($"uid")
.agg(
max("imei").as("imei"),
max("imsi").as("imsi"),
max("mac").as("mac"),
max("uuid").as("uuid"),
max("androidId").as("androidId"),
max("deviceId").as("deviceId")
) noUid.createTempView("nouid")
uids.createTempView("uids") // 开发一个自定义函数,用来判断两个字符串在非空情况下是否相等
val is_equal = (x: String, y: String) => {
if (x != y || StringUtils.isBlank(x) || StringUtils.isBlank(y)) false else true
}
spark.udf.register("is_equal",is_equal) // 对没有uid的数据进行回补操作
val part1: DataFrame = spark.sql(
"""
|
|select
|
|a.eventid ,
|a.timestamp ,
|a.event ,
|if(b.uid is not null,b.uid,a.uid) as uid,
|a.phoneNbr ,
|a.sessionId ,
|a.imei ,
|a.mac ,
|a.imsi ,
|a.osName ,
|a.osVer ,
|a.androidId ,
|a.resolution ,
|a.deviceType ,
|a.deviceId ,
|a.uuid ,
|a.appid ,
|a.appVer ,
|a.release_ch ,
|a.promotion_ch ,
|a.longtitude ,
|a.latitude ,
|a.carrier ,
|a.netType ,
|a.cid_sn ,
|a.ip ,
|a.province,
|a.city,
|a.district,
|a.dateStr,
|a.timeStr
|
|from
|
|nouid a left join uids b
| on is_equal(a.imei,b.imei)
| or is_equal(a.imsi,b.imsi)
| or is_equal(a.mac,b.mac)
| or is_equal(a.uuid,b.uuid)
| or is_equal(a.androidId,b.androidId)
| or is_equal(a.deviceId,b.deviceId)
|
""".stripMargin) // 将回补好的数据 union 原来就有uid的数据
val result = haveUid.toDF.union(part1) // 输出结果
result.write.parquet("E:\\javafile\\dataware1\\2019-10-29") spark.close() }
}
大数据学习day39----数据仓库02------1. log4j 2. 父子maven工程(子spring项目的创建)3.项目开发(埋点日志预处理-json数据解析、清洗过滤、数据集成实现、uid回补)的更多相关文章
- Maven 工程下 Spring MVC 站点配置 (二) Mybatis数据操作
详细的Spring MVC框架搭配在这个连接中: Maven 工程下 Spring MVC 站点配置 (一) Maven 工程下 Spring MVC 站点配置 (二) Mybatis数据操作 这篇主 ...
- spring 学习(一):使用 intellijIDEA 创建 maven 工程进行 Spring ioc 测试
spring学习(一):使用 intellijIDEA 创建 maven 工程进行 Spring ioc 测试 ioc 概念 控制反转(Inversion of Control,缩写为IOC),是面向 ...
- Django项目的创建与介绍.应用的创建与介绍.启动项目.pycharm创建启动项目.生命周期.三件套.静态文件.请求及数据.配置Mysql完成数据迁移.单表ORM记录的增删改查
一.Django项目的创建与介绍 ''' 安装Django #在cmd中输入pip3 #出现这个错误Fatal error in launcher: Unable to create process ...
- 大数据学习(12)—— Hive Server2服务
什么是Hive Server2 上一篇我们启动了hive --service metastore服务,可以通过命令行来访问hive服务,但是它不支持多客户端同时访问,参见官网说明:HiveServer ...
- 大数据学习之Linux进阶02
大数据学习之Linux进阶 1-> 配置IP 1)修改配置文件 vi /sysconfig/network-scripts/ifcfg-eno16777736 2)注释掉dhcp #BOOTPR ...
- java 与大数据学习较好的网站
C# C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!https://www.cnblogs.com/doforfuture/p/6293926.html ...
- 大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 图文详解
引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单 ...
- 大数据学习之Linux基础01
大数据学习之Linux基础 01:Linux简介 linux是一种自由和开放源代码的类UNIX操作系统.该操作系统的内核由林纳斯·托瓦兹 在1991年10月5日首次发布.,在加上用户空间的应用程序之后 ...
- 大数据系列之数据仓库Hive安装
Hive系列博文,持续更新~~~ 大数据系列之数据仓库Hive原理 大数据系列之数据仓库Hive安装 大数据系列之数据仓库Hive中分区Partition如何使用 大数据系列之数据仓库Hive命令使用 ...
随机推荐
- linux 启动过程以及如何将进程加入开机自启
linux 启动流程 系统启动主要顺序就是: 1. 加载内核 2. 启动初始化进程 3. 确定运行级别 4. 加载开机启动程序 5. 用户登录 启动流程的具体细节可以看看Linux 的启动流程 第4步 ...
- 大一C语言学习笔记(4)---自省篇
博主"曾经"做过的傻事: #你有的*没打全 #你用/的时候没考虑()是一对的 #printf随后加\n #所有变量只要用,就一定要定义数据类型 #sqrt()代表根号 #inclu ...
- Part 38 AngularJS cancel route change
n this video we will discuss, how to cancel route change in Angular with an example. This is extreme ...
- python实现超大图像的二值化方法
一,分块处理超大图像的二值化问题 (1) 全局阈值处理 (2) 局部阈值 二,空白区域过滤 三,先缩放进行二值化,然后还原大小 np.mean() 返回数组元素的平均值 np.std() 返回数 ...
- [atAGC052C]Nondivisible Prefix Sums
当1为$a_{i}$中出现次数最多的元素(之一),则有以下结论-- 结论:$a_{i}$合法当且仅当$P\not\mid \sum_{i=1}^{n}a_{i}$且$\sum_{i=1}^{n}[a_ ...
- [cf1491F]Magnets
首先,只需要找到一个有磁性的位置,就可以通过$n-1$次判断其余磁铁是否有磁性,因此也就是要在$\lfloor\log_{2}n\rfloor+1$次中找到一个有磁性的位置 有一个$n-1$次的做法, ...
- 面试官问我JVM调优,我忍不住了!
面试官:今天要不来聊聊JVM调优相关的吧? 面试官:你曾经在生产环境下有过调优JVM的经历吗? 候选者:没有 面试官:... 候选者:嗯...是这样的,我们一般优化系统的思路是这样的 候选者:1. 一 ...
- 7.3 自定义镜像-运行nginx与tomcat并结合PV/PVC/NFS以实现动静分离示例
1.在NFS SERVER上为tomcat.nginx创建相关目录 NFS SERVER的部署配置参考:https://www.cnblogs.com/yanql/p/15410308.html 1. ...
- postman自动调用获取token
Postman不光支持单次请求,还支持环境变量.全局变量.集合变量 本文使用Collection Variable Collection 如下图可以点击Collection然后可以添加请求和文件夹,以 ...
- 【洛谷1340】兽径管理(最小生成树 Kruskal)(sort的一些技巧)【2012福建省信息学奥林匹克CCF NOIP夏令营第05天训练】
Description 约翰农场的牛群希望能够在 N 个(1<=N<=6000) 草地之间任意移动.草地的编号由 1到 N.草地之间有树林隔开.牛群希望能够选择草地间的路径,使牛群能够从任 ...