step 6 需求二:广告点击量实时统计

描述:实时统计每天各地区各城市各广告的点击总流量,并将其存入 MySQL。

原方案:

1)单个批次内对数据进行按照天维度的聚合统计;

2)结合 MySQL 数据跟当前批次数据更新原有的数据。

新方案:

1)通过kafka获取黑名单过滤后的数据

2)按照时间、地市、城市聚合统计点击量

3)更新时间、地市、城市、点击量入库到mysql

1 建表

CREATE TABLE area_city_ad_count (
time VARCHAR(30),
area VARCHAR(30),
city VARCHAR(30),
prov VARCHAR(30),
adid VARCHAR(50),
 count BIGINT,
PRIMARY KEY (time,area,prov,city,adid)
);


ALTER DATABASE mysql CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE area_city_ad_count CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

2 从consumerApp获取黑名单过滤后的日志

//获取黑名单过滤后的日志信息
val filterClickData: InputDStream[ConsumerRecord[String, String]] = 
dao.getKafkaData("clickTopic")

3 consumerApp增加kafka数据分发

def dataDealMain(): Unit = {
 val debugFlag=false
 val clickLogData: DStream[ClickLog] = getKafkaData
 if (debugFlag) {
  println("kafka数据获取如下")
  clickLogData.print()
 }
 //过滤黑名单用户,返回过滤后的日志
 val filterBlack: DStream[ClickLog] = filterBlackUser(clickLogData).cache()
 //过滤后数据传给城市点击统计模块
  filterBlack.foreachRDD(
  rdd=>{
   rdd.foreachPartition(
    (iter: Iterator[ClickLog]) => {
     iter.foreach(
      (data: ClickLog) =>{
       dao.setDataToKafka("clickTopic",Array(data.time,data.userId,data.cityId,data.cityName,data.provName,data.advAddres))
      }
     )
    })
  }
 )

 //计算每日点击量,写入mysql,每天点击量超限,拉入黑名单
 addUserClickInfo(filterBlack)
 sscGet().start()
 sscGet().awaitTermination()
 
}

4 对过滤后的数据根据时间,城市,广告聚合统计点击次数

//按照天的维度统计点击量
val reduceData: DStream[Array[Any]] = filterClickData.transform(
 rdd => {
  rdd.map((data: ConsumerRecord[String, String]) => {
   data.value().split(",")
  }).map((data: Array[String]) => {
   /*
   Array(data.time,data.userId,data.cityId,data.cityName,data.provName,data.advAddres)
   =>((data.time,data.cityId,data.cityName,data.provName,data.advAddres),count)
   * */
   ((data(0), data(2), data(3), data(4), data(5)), 1)
  }).reduceByKey(_ + _).map(data =>{
   Array(data._1._1,data._1._2,data._1._3,data._1._4,data._1._5,data._2,data._2)
  })
 }
)

5 数据入库


reduceData.foreachRDD(rdd => {
 rdd.foreachPartition(rddPartition => {
  val mysqlConn = mysqlConnect()
  rddPartition.foreach(
   (data: Array[Any]) => {
    //建立mysql链接
    dao.executeOneData(mysqlConn, sql, data, true)
   }
  )
  mysqlConn.close()
 })
 
}

6 报错解决

6.1   DStream执行rdd操作之后未生效

原因:忘了需要执行类似于行动算子的输出

6.2 mysql数据插入报错

Incorrect string value: '\xE6\xB7\xAE\xE5\x8C\x97'

原因:

你试图插入的字符串是'\xE6\xB7\xAE\xE5x8C\x97',这是一个UTF-8编码的中文字符串,对应的汉字是"你好"。然而,如果你的数据库表或列的字符集编码设置为不支持这种UTF-8编码的字符,那么当你尝试插入这个字符串时,就会出现“Incorrect string value”的错误。

解决这个问题的方法通常是更改你的数据库表或列的字符集编码,使其能够支持你想要插入的字符。例如,你可以将字符集编码更改为MySQL支持的utf8mb4,它支持更多的Unicode字符,包括一些特殊的中文字符。

解决办法:

ALTER DATABASE mysql CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE area_city_ad_count CONVERT TO CHARACTER SET utf8mb4 CO

6.3 mysql连接过多报错

SparkStreaming Data source rejected establishment of connection,  message from server: Too many connections

原因:产生了太多了mysql连接,且都是sleep状态

SELECT COUNT(*) FROM information_schema.PROCESSLIST;--查看连接的数量

step 6 需求二:广告点击量实时统计_第1张图片

解决办法:

1、首先需要在代码中增加连接关闭

mysqlConn.close()

2、设置interactive_timeout和wait_timeout

超过时间的sleep连接会自动杀掉

set global interactive_timeout=10;

set global wait_timeout=10;

3、设置资源池来管理连接

 

你可能感兴趣的:(kafka,分布式,spark,mysql,大数据)