1 准备数据
本项目的数据是采集自电商的用户行为数据.
主要包含用户的 4 种行为: 搜索, 点击, 下单和支付.
数据格式如下, 不同的字段使用下划线分割开_:
数据说明:
- 数据采用 _ 分割字段
- 每一行表示用户的一个行为, 所以每一行只能是四种行为中的一种.
- 如果搜索关键字是 null, 表示这次不是搜索
- 如果点击的品类 id 和产品 id 是 -1 表示这次不是点击
- 下单行为来说一次可以下单多个产品, 所以品类 id 和产品 id 都是多个, id 之间使用逗号,分割. 如果本次不是下单行为, 则他们相关数据用null来表示
- 支付行为和下单行为类似.
2.需求 1: Top10 热门品类
2.1 需求分析
按照每个品类的 点击、下单、支付 的量来统计热门品类.
创建3个包:
- acc :存累加器
- app :要实现的功能
- bean :封装数据
2.2 思路
使用累加器:
- 遍历全部日志数据, 根据品类 id 和操作类型分别累加. 需要用到累加器
- 定义累加器
- 当碰到订单和支付业务的时候注意拆分字段才能得到品类 id
- 遍历完成之后就得到每个每个品类 id 和操作类型的数量.
- 按照点击下单支付的顺序来排序
- 取出 Top10
2.3 代码实现
2.3.1 整体入口 ProjectApp
import com.jaffe.sparkcore.project.bean.{
CategoryCount, UserVisitAction}
import org.apache.spark.{
SparkConf, SparkContext}
/**
* @Author jaffe
* @Date 2020/05/11 10:19
*/
object ProjectApp {
def main(args: Array[String]): Unit = {
//1.读数据
val conf = new SparkConf().setAppName("ProjectApp").setMaster("local[2]")
val sc = new SparkContext(conf)
val sourceRDD = sc.textFile("E:\\bigdata\\text\\input\\spark\\project\\user_visit_action.txt")
//2.封装到样例类
val userVisitActionRDD = sourceRDD.map(line => {
val splits = line.split("_")
UserVisitAction(
splits(0),
splits(1).toLong,
splits(2),
splits(3).toLong,
splits(4),
splits(5),
splits(6).toLong,
splits(7).toLong,
splits(8),
splits(9),
splits(10),
splits(11),
splits(12).toLong)
})
//3. 需求1的分析 返回值是top10 的品类id
val categoryCountList:List[CategoryCount] = CategoryTopApp.calcCategoryTop10(sc, userVisitActionRDD)
sc.stop()
}
}
2.3.2 封装用户行为数据 UserVisitAction
package com.jaffe.sparkcore.project.bean
/**
* @Author jaffe
* @Date 2020/05/11 10:28
*/
/**
* 用户访问动作表
*
* @param date 用户点击行为的日期
* @param user_id 用户的ID
* @param session_id Session的ID
* @param page_id 某个页面的ID
* @param action_time 动作的时间点
* @param search_keyword 用户搜索的关键词
* @param click_category_id 某一个商品品类的ID
* @param click_product_id 某一个商品的ID
* @param order_category_ids 一次订单中所有品类的ID集合
* @param order_product_ids 一次订单中所有商品的ID集合
* @param pay_category_ids 一次支付中所有品类的ID集合
* @param pay_product_ids 一次支付中所有商品的ID集合
* @param city_id 城市 id
*/
case class UserVisitAction(date: String,
user_id: Long,
session_id: String,
page_id: Long,
action_time: String,
search_keyword: String,
click_category_id: Long,
click_product_id: Long,
order_category_ids: String,
order_product_ids: String,
pay_category_ids: String,
pay_product_ids: String,
city_id: Long)
2.3.3 定义用到的累加器 CategoryAcc
需要统计每个品类的点击量, 下单量和支付量, 所以我们在累加器中使用 Map 来存储这些数据: Map(cid-> (click, order,pay))
import com.jaffe.sparkcore.project.bean.UserVisitAction
import org.apache.spark.util.AccumulatorV2
import scala.collection.mutable
/**
* @Author jaffe
* @Date 2020/05/11 10:35
*/
class CategoryAcc extends AccumulatorV2[UserVisitAction, mutable.Map[String, (Long, Long, Long)]] {
//可变map使用val
private val map = mutable.Map[String, (Long, Long, Long)]()
//判零,判断集合是否为空
override def isZero: Boolean = map.isEmpty
//复制累加器
override def copy(): AccumulatorV2[UserVisitAction, mutable.Map[String, (Long, Long, Long)]] = {
val acc = new CategoryAcc
//java:sychronized(锁){}
acc.map.synchronized {
acc.map ++= this.map
}
acc
}
//重置累加器
override def reset(): Unit = {
//可变累加器,重置就是清空
map.clear()
}
//累加(分区内聚合)
override def add(v: UserVisitAction): Unit = {
/*
用户行为有可能是点击,有可能是搜索、下单、支付
*/
v match {
//判断是否点击
case action if action.click_category_id != -1 =>
//点击的品类id