Scala_Spark-电商平台离线分析项目-需求七广告黑名单实时统计
第四模块:广告流量实时统计统计
技术点:SparkStreaming、kafka集群
补充知识点:
- DStream中:
- foreachRDD作用于DStream中每一个时间间隔的RDD,
- foreachPartition作用于每一个时间间隔的RDD中的每一个partition,
- foreach作用于每一个时间间隔的RDD中的每一个元素。
kafka.broker.list=node01:9092,node02:9092,node03:9092
kafka.topics=AdRealTimeLog0308、
(一)执行步骤
1)本地生产数据 发送到kafka
-
开启zookeeper集群
【三台服务器启动zookeeper】,三台机器都执行以下命令启动zookeeper
cd /export/servers/zookeeper-3.4.5-cdh5.14.0
bin/zkServer.sh start
进程QuorumPeerMain -
开启kafka集群
【启动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 & -
node01开启一个消费者
所有配置完成的情况下
[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]. // 等待消费
-
本地运行模拟生产实时数据文件MockRealTimeData.scala
该需求运行前可以把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
// 以上部分和上个笔记一样
2)IDEA运行AdverStat.scala
-
在以上试验成功状态下,IDEA运行AdverStat.scala文件
运行成功后不断去数据库中刷新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
/*
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后