Spark Streaming 项目实战 (3) | 得到每天每地区热门广告Top3并写入到redis

  大家好,我是不温卜火,是一名计算机学院大数据专业大二的学生,昵称来源于成语—不温不火,本意是希望自己性情温和。作为一名互联网行业的小白,博主写博客一方面是为了记录自己的学习过程,另一方面是总结自己所犯的错误希望能够帮助到很多和自己一样处于起步阶段的萌新。但由于水平有限,博客中难免会有一些错误出现,有纰漏之处恳请各位大佬不吝赐教!暂时只有csdn这一个平台,博客主页:https://buwenbuhuo.blog.csdn.net/

  本片博文为大家带来的是Spark Streaming 项目实战 (3) | 得到每天每地区热门广告 Top3并写入到redis中。
1


2
  最终数据的格式: 存储在 redis 中, 使用 hash 存储
3
针对于此,我们采取的步骤是
5

一. 计算每天每地区每广告的点击量 并取前三

  • 1. 在AreaTopAPP中添加如下内容
   // 先计算每天每地区每广告的点击量
    val dayAreaGrouped: DStream[((String, String), Iterable[(String, Int)])] = adsInfoStream
      .map(info => ((info.dayString, info.area, info.adsId), 1)) // ((天, 地区, 广告), 1)
      .updateStateByKey((seq: Seq[Int], option: Option[Int]) => { // 先计算每天每地区每广告的点击量
        Some(seq.sum + option.getOrElse(0))
      }) // ((天, 地区, 广告), 1000)
      .map { // map出来(day,area)作为key
        case ((day, area, ads), count) =>
          ((day, area), (ads, count))
      }
      .groupByKey()
    // 每组内排序,取前3
    val result: DStream[((String, String), List[(String, Int)])] = dayAreaGrouped.map {
      case (key, it: Iterable[(String, Int)]) =>
        (key, it.toList.sortBy(-_._2).take(3))
    }

    result.print(100)

仅仅添加这些还不够,如果不添加检查点的话,会出现报错

  • 2. 在主类APP中添加检查点

4

  • 3. 运行并查看结果

4

二. 将数据写入到redis

2.1 准备工作

  在将数据写入到redis之前,我们需要先修改配置

  • 1. 需要修改的三个配置

5

  • 2. 添加依赖
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.3</version>
        </dependency>

6

2.2 测试运行

  • 1. 新建工具类RedisUtil
package com.buwenbuhuo.streaming.project.util
import redis.clients.jedis.{JedisPool,JedisPoolConfig}
/*
 *
 * @author 不温卜火
 * @create 2020-08-15 13:13
 * MyCSDN :  https://buwenbuhuo.blog.csdn.net/
 *
 */
object RedisUtil {

  private val conf: JedisPoolConfig = new JedisPoolConfig
  conf.setMaxTotal(100)
  conf.setMaxIdle(10)
  conf.setMinIdle(10)
  conf.setBlockWhenExhausted(true)   // 忙碌时间是否等待
  conf.setMaxWaitMillis(1000)        // 最大等待时间
  conf.setTestOnBorrow(true)
  conf.setTestOnReturn(true)

  val pool: JedisPool = new JedisPool(conf,"hadoop002",6379)

  def getClient = pool.getResource

}
/*
1. 使用连接池创建客户端

2. 直接创建客户端
 */
  • 2. 在AreaTopAPP类中添加如下内容
    // 把数据写入到redis
    result.foreachRDD(rdd => {
      rdd.foreachPartition((it:Iterator[((String,String),List[(String,Int)])]) =>{
        // 1. 建立到 redis 的连接
        val client: Jedis = RedisUtil.getClient
        // 2. 写数据到redis
        it.foreach{
          // ((2020-08-15,华北),List((1,12), (2,7), (4,7)))
          case ((day,area),adsCountList) =>
            val key = "area:ads:count" + day
            val field = area
            // 把结合转换成json字符串 json4s
            // 专门用于吧集合转成字符串(样例类不行)
            import org.json4s.JsonDSL._
            // list 集合转成 json 字符串
            val value = JsonMethods.compact(JsonMethods.render(adsCountList))
            client.hset(key,field,value)
        }
        // 3. 关闭到redis的连接
        client.close()  // 其实是吧这个客户端还给连接池
      })
    })
  • 3. 测试运行结果并查看是否写入到redis中

7
8
如上图我们可以看到,存入的有数据,但是这些数据不是我们想看的,我们可以根据一下方式进行解决

// --raw   使数据以我们想要的类型转换出来
[bigdata@hadoop002 myredis]$ redis-cli --raw

9

三. 完整代码

在上篇博文中,有的类已经存在了,因此此部分只是把新的类和更新的类写出来,剩下的如有需要可在上篇中自行复制粘贴

  • 1. AreaTopAPP(更新)
package com.buwenbuhuo.streaming.project.app
import com.buwenbuhuo.streaming.project.bean.AdsInfo
import org.apache.spark.streaming.dstream.DStream


/**
 *
 * @author 不温卜火
 * @create 2020-08-14 13:41
 * MyCSDN :  https://buwenbuhuo.blog.csdn.net/
 *
 */
object AreaTopAPP extends App {
  override def doSomething(adsInfoStream: DStream[AdsInfo]): Unit = {
    // 打印数据到控制台
//    adsInfoStream.print(1000)

    // 先计算每天每地区每广告的点击量
    val dayAreaGrouped: DStream[((String, String), Iterable[(String, Int)])] = adsInfoStream
      .map(info => ((info.dayString, info.area, info.adsId), 1)) // ((天, 地区, 广告), 1)
      .updateStateByKey((seq: Seq[Int], option: Option[Int]) => { // 先计算每天每地区每广告的点击量
        Some(seq.sum + option.getOrElse(0))
      }) // ((天, 地区, 广告), 1000)
      .map { // map出来(day,area)作为key
        case ((day, area, ads), count) =>
          ((day, area), (ads, count))
      }
      .groupByKey()
    // 每组内排序,取前3
    val result: DStream[((String, String), List[(String, Int)])] = dayAreaGrouped.map {
      case (key, it: Iterable[(String, Int)]) =>
        (key, it.toList.sortBy(-_._2).take(3))
    }

    result.print(100)
    
    import com.buwenbuhuo.streaming.project.util.RealUtil._
    result.saveToRedis




  }
}
/*
每天每地区热门广告 Top3

1. 先计算每天每地区每广告的点击量
    ((day,area,ads),1) => updateStateByKey

2.  按照每天每地区分组

3.  每组内排序,取前3

4.  把数据写入到redis

数据类型:
    k-v 形式数据库(nosql 数据)
    k: 都是字符串
    V 的数据类型
        5大数据类型
          1. string
          2. set 不重复
          3. list 允许重复
          4. hash map,存的是kv
          5. zset
---------
((2020-08-15,华北),List((1,12), (2,7), (4,7)))
((2020-08-15,华东),List((5,9), (4,7), (2,5)))
((2020-08-14,华北),List((1,4), (5,3), (4,2)))
((2020-08-15,华中),List((3,5), (2,3), (5,3)))
---------
选择什么类型的数据:
每天一个key
key                               value
"area:ads:count"  + day           hash
                                  field        value
                                  area         json字符串
                                  "华中"       {3:14,1:12,2:8}

 */
  • 2. RedisUtil(新)
package com.buwenbuhuo.streaming.project.util
import redis.clients.jedis.{JedisPool,JedisPoolConfig}
/*
 *
 * @author 不温卜火
 * @create 2020-08-15 13:13
 * MyCSDN :  https://buwenbuhuo.blog.csdn.net/
 *
 */
object RedisUtil {

  private val conf: JedisPoolConfig = new JedisPoolConfig
  conf.setMaxTotal(100)
  conf.setMaxIdle(10)
  conf.setMinIdle(10)
  conf.setBlockWhenExhausted(true)   // 忙碌时间是否等待
  conf.setMaxWaitMillis(1000)        // 最大等待时间
  conf.setTestOnBorrow(true)
  conf.setTestOnReturn(true)

  val pool: JedisPool = new JedisPool(conf,"hadoop002",6379)

  def getClient = pool.getResource

}
/*
1. 使用连接池创建客户端

2. 直接创建客户端
 */
  • 3. RealUtil(新)
package com.buwenbuhuo.streaming.project.util
import org.apache.spark.streaming.dstream.DStream
import org.json4s.jackson.JsonMethods
import redis.clients.jedis.Jedis


/**
 *
 * @author 不温卜火
 * @create 2020-08-15 14:13
 * MyCSDN :  https://buwenbuhuo.blog.csdn.net/
 *
 */
object RealUtil {
      implicit class MyRedis(stream: DStream[((String,String),List[(String,Int)])]){

        def saveToRedis = {
          stream.foreachRDD(rdd => {
            rdd.foreachPartition((it:Iterator[((String,String),List[(String,Int)])]) =>{
              // 1. 建立到 redis 的连接
              val client: Jedis = RedisUtil.getClient
              // 2. 写数据到redis
              it.foreach{
                // ((2020-08-15,华北),List((1,12), (2,7), (4,7)))
                case ((day,area),adsCountList) =>
                  val key = "area:ads:count" + day
                  val field = area
                  // 把结合转换成json字符串 json4s
                  // 专门用于吧集合转成字符串(样例类不行)
                  import org.json4s.JsonDSL._
                  // list 集合转成 json 字符串
                  val value = JsonMethods.compact(JsonMethods.render(adsCountList))
                  client.hset(key,field,value)
              }
              // 3. 关闭到redis的连接
              client.close()  // 其实是吧这个客户端还给连接池
            })
          })
        }
      }
}

  本次的分享就到这里了,


14

  好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
  如果我的博客对你有帮助、如果你喜欢我的博客内容,请“点赞” “评论”“收藏”一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
  码字不易,大家的支持就是我坚持下去的动力。点赞后不要忘了关注我哦!

15
16

猜你喜欢

转载自blog.csdn.net/qq_16146103/article/details/108011134