第四模块:广告流量实时统计统计
技术点:SparkStreaming、kafka集群
补充知识点:
kafka.broker.list=node01:9092,node02:9092,node03:9092
kafka.topics=AdRealTimeLog0308、
【三台服务器启动zookeeper】,三台机器都执行以下命令启动zookeeper
cd /export/servers/zookeeper-3.4.5-cdh5.14.0
bin/zkServer.sh start
进程QuorumPeerMain
【启动kafka集群】默认端口9092
三台机器启动kafka服务
[root@node01 servers]# cd /export/servers/kafka_2.11-1.0.0/
前台启动 ./kafka-server-start.sh …/config/server.properties
后台启动命令 nohup bin/kafka-server-start.sh config/server.properties > /dev/null 2>&1 &
所有配置完成的情况下
[root@node01 ~]# kafka-console-consumer.sh --zookeeper node01:2181 --topic AdRealTimeLog0308
Using the ConsoleConsumer with old consumer is deprecated and will be removed in a future major release. Consider using the new consumer by passing [bootstrap-server] instead of [zookeeper].
// 等待消费
该需求运行前可以把id数和秒数改小一点 不然要等很久才能过100次
// node01上出现 集群上消费成功 代表生产数据发送到kafka没有问题
1573145438221 3 3 93 5
1573145438221 6 6 87 17
1573145438221 0 0 10 16
1573145438221 7 7 11 15
1573145438221 0 0 8 18
1573145438221 0 0 97 1
// 以上部分和上个笔记一样
代码在附
运行成功后不断去数据库中刷新ad_user_click_count表 数字在不断变化 黑名单表也会变化,所有用户都加入了黑名单
其中主要方法部分如下:
实时维护黑名单方法
/**
* 锚点1的方法
* 实时维护黑名单
* @param adRealTimeFilterDStream 不在黑名单里的所有实时数据 timestamp province city userid adid
* DStream[RDD[String]]
* @return (key,1L)
*/
def generateBlackList(adRealTimeFilterDStream: DStream[String]) = {
// 数据准备(key,1)
val key2NumDStream = adRealTimeFilterDStream.map{ // map算子
// timestamp province city userid adid
case log=>{
val logSplit = log.split(" ")
val timeStamp = logSplit(0).toLong
// yy-mm-dd
val dateKey = DateUtils.formatDateKey(new Date(timeStamp)) //formatDateKey(Date类型)
val userId = logSplit(3).toLong
val adid = logSplit(4).toLong
val key = dateKey+"_"+userId+"_"+adid
(key,1L) // 为reduceByKey做准备
} //end case
}
// todo 根据key聚合得到点击次数
val key2CountDStream = key2NumDStream.reduceByKey(_+_) // (key1,12)
// todo 根据每一个RDD里面的数据,更新用户点击次数表
key2CountDStream.foreachRDD{
rdd => rdd.foreachPartition{ // 这里模糊 在foreachRDD里再进行foreachPartition 原因:DStream里面是一批RDD
items =>
val clickCountArray = new ArrayBuffer[AdUserClickCount]() // 通常用ArrayBuffer来做容器 变长数组
for((key,count) <- items){ // scala中的增强for循环 items是一个迭代器
// 相当于把之前为了reduceByKey聚合的key重新复原成表
val keySplit = key.split("_")
val date = keySplit(0)
val userId = keySplit(1).toLong
val adid = keySplit(2).toLong
clickCountArray += AdUserClickCount(date,userId,adid,count) // 把对象添加到容器里
}
// 把现在的点击次数更新到用户点击次数表里
AdUserClickCountDAO.updateBatch(clickCountArray.toArray)
}
} // 更新完毕
// todo 把我们这个用户点击次数表里最新的黑名单用户取出来
// key2BlackListDStream:DStream[RDD(key,count)]
val key2BlackListDStream=key2CountDStream.filter{ // filter算子期待一个布尔类型返回值
case (key,count) =>
val keySplit = key.split("_")
val date = keySplit(0)
val userId = keySplit(1).toLong
val adid = keySplit(2).toLong
// 调用函数根据date userId adid获得相应的原始clickCount
val clickCount = AdUserClickCountDAO.findClickCountByMultiKey(date,userId,adid)
if(clickCount > 100){
true // true 就留下
}else{
false
}
}
// todo 最新的黑名单id追加到黑名单里
// key2BlackListDStream.map: DStream[RDD[userId]]
val userIdDStream = key2BlackListDStream.map{ // 仔细想一下.map算子的作用
case (key,count) => key.split("_")(1).toLong
}.transform(rdd=>rdd.distinct()) // 去重
userIdDStream.foreachRDD{
rdd => rdd.foreachPartition{
items =>
// 每一个RDD
val userIdArray = new ArrayBuffer[AdBlacklist]() // 容器
for(userId <- items){
userIdArray += AdBlacklist(userId) // 把实例添加进容器
}
// 调用方法批量插入广告黑名单用户 写入mysql 这个类方法一步一步追上去 我可太南了
AdBlacklistDAO.insertBatch(userIdArray.toArray) // 变成定长数组再传进去
}
}
// 需求七结束
main方法
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setAppName("adverstat").setMaster("local[*]")
val sparkSession = SparkSession.builder().config(sparkConf).enableHiveSupport().getOrCreate()
// 标准应当是 val streamingContext = StreamingContext.getActiveOrCreate(checkpointDir,func)
val streamingContext = new StreamingContext(sparkSession.sparkContext,Seconds(5))
// 配置kafka相关信息
val kafka_brokers = ConfigurationManager.config.getString(Constants.KAFKA_BROKERS) //node01:9092,node02:9092,node03:9092
val kafka_topics = ConfigurationManager.config.getString(Constants.KAFKA_TOPICS) //kafka.topics=AdRealTimeLog0308
// kafka配置信息
val kafkaParam = Map(
"bootstrap.servers" -> kafka_brokers,
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "group1",
// auto.offset.reset
// latest: 先去Zookeeper获取offset,如果有,直接使用,如果没有,从最新的数据开始消费
// earlist: 先去zookeeper获取offset,如果有直接使用,如果没有,从最开始的数据开始消费
// none: 先去Zookeeper获取offset,如果有,直接使用,如果没有,直接报错
"auto.offset.reset" -> "latest",
"enable.auto.commit" -> (false:java.lang.Boolean)
)
// 创建DStream
// 从kafka中消费数据 拿到的每一条数据都是messege,里面是key-value
val adRealTimeDStream = KafkaUtils.createDirectStream[String,String](
streamingContext,
// 让kafka分区均匀地在excutor上分配 有三种选择
LocationStrategies.PreferConsistent,
// 消费者订阅
ConsumerStrategies.Subscribe[String,String](Array(kafka_topics),kafkaParam)
)
// 取出了DStream里面每一条数据的value值
// adReadTimeValueDStream:DStream[RDD RDD RDD ...] RDD[String]
// String: timestamp province city userid adid
val adReadTimeValueDStream = adRealTimeDStream.map(item => item.value())
// adRealTimeFilterDStream 所有不在黑名单里的实时数据都在里面了
val adRealTimeFilterDStream =adReadTimeValueDStream.transform{
logRDD => {
// blackListArray:Array[AdBlacklist] AdBlacklist:userId
val blackListArray = AdBlacklistDAO.findAll() // 这里连接了数据库 创建了mySqlPool
// userIdArray:Array[Long] [userId1,userId2,...]
var userIdArray = blackListArray.map(item => item.userid)
// 过滤掉已经存在在黑名单里的
logRDD.filter {
// log: timestamp province city userid adid
case log =>
val logSplit = log.split(" ")
val userId = logSplit(3).toLong
!userIdArray.contains(userId)
}
} // end logRDD
} //end adRealTimeFilterDStream
// 测试 本地能否消费kafka集群的数据
// adRealTimeFilterDStream.foreachRDD(rdd=>rdd.foreach(println(_)))
// todo 第一步:实时维护黑名单
// 1
generateBlackList(adRealTimeFilterDStream)
streamingContext.start()
streamingContext.awaitTermination()
}
/*
ad.sql
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50720
Source Host : localhost
Source Database : commerce
Target Server Type : MySQL
Target Server Version : 50720
File Encoding : utf-8
Date: 11/03/2017 11:23:32 AM
*/
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for `ad_blacklist`
-- ----------------------------
DROP TABLE IF EXISTS `ad_blacklist`;
CREATE TABLE `ad_blacklist` (
`userid` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `ad_click_trend`
-- ----------------------------
DROP TABLE IF EXISTS `ad_click_trend`;
CREATE TABLE `ad_click_trend` (
`date` varchar(30) DEFAULT NULL,
`hour` varchar(30) DEFAULT NULL,
`minute` varchar(30) DEFAULT NULL,
`adid` int(11) DEFAULT NULL,
`clickCount` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `ad_province_top3`
-- ----------------------------
DROP TABLE IF EXISTS `ad_province_top3`;
CREATE TABLE `ad_province_top3` (
`date` varchar(30) DEFAULT NULL,
`province` varchar(100) DEFAULT NULL,
`adid` int(11) DEFAULT NULL,
`clickCount` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `ad_stat`
-- ----------------------------
DROP TABLE IF EXISTS `ad_stat`;
CREATE TABLE `ad_stat` (
`date` varchar(30) DEFAULT NULL,
`province` varchar(100) DEFAULT NULL,
`city` varchar(100) DEFAULT NULL,
`adid` int(11) DEFAULT NULL,
`clickCount` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `ad_user_click_count`
-- ----------------------------
DROP TABLE IF EXISTS `ad_user_click_count`;
CREATE TABLE `ad_user_click_count` (
`date` varchar(30) DEFAULT NULL,
`userid` int(11) DEFAULT NULL,
`adid` int(11) DEFAULT NULL,
`clickCount` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
test后