Scala_Spark-电商平台离线分析项目-需求七广告黑名单实时统计

14 篇文章 0 订阅
13 篇文章 0 订阅

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

  1. 开启zookeeper集群

    【三台服务器启动zookeeper】,三台机器都执行以下命令启动zookeeper
    cd /export/servers/zookeeper-3.4.5-cdh5.14.0
    bin/zkServer.sh start
    进程QuorumPeerMain

  2. 开启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 &

  3. 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].
    
    // 等待消费
    
    
  4. 本地运行模拟生产实时数据文件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
    

// 以上部分和上个笔记一样

  1. 提前在本地建立的commerce数据库创建好5张ad广告表

    代码在附

2)IDEA运行AdverStat.scala

  1. 在以上试验成功状态下,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后

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值