头歌 计算排行榜并实时输出 第1关:计算排行榜并实时输出

目录

任务描述

相关知识

计算排行榜并实时输出

编程要求

测试说明

参考答案


任务描述

本关任务:编写一个能计算排行榜并实时输出的小程序。

相关知识

为了完成本关任务,你需要掌握:如何计算排行榜并实时输出。

计算排行榜并实时输出

对于实时数据,我们之前进行了基本的计算,但是我们还要用 Flink 的方式来实时获得数据和实时输出数据,以及用 Flink 的方式来计算实时数据。

代码及详细解释:

 
  1. //输入数据
  2. case class UserBehaviorL1( userId: Long, itemId: Long, categoryId: Int, behavior: String, timestamp: Long )
  3. //输出数据
  4. case class ItemViewCountL1( itemId: Long, windowEnd: Long, count: Long )
  5. object LiLun1 {
  6. def main(args: Array[String]): Unit = {
  7. val properties = new Properties()
  8. //flink的流执行环境
  9. val env = StreamExecutionEnvironment.getExecutionEnvironment
  10. //设定Time类型为EventTime
  11. env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
  12. //并发为1
  13. env.setParallelism(1)
  14. //获得数据源的文件
  15. val stream = env
  16. .readTextFile("D:\\MyProjects\\UserBehaviorL1Analysis\\HotItemsAnalysis\\src\\main\\resources\\UserBehavior1.csv")
  17. //获得文件中的每一行记录
  18. .map(line => {
  19. val linearray = line.split(",")
  20. UserBehaviorL1( linearray(0).toLong, linearray(1).toLong, linearray(2).toInt, linearray(3), linearray(4).toLong )
  21. })
  22. //指定时间戳
  23. .assignAscendingTimestamps(_.timestamp * 1000)
  24. //进行过滤,只获得pv行为的数据
  25. .filter(_.behavior == "pv")
  26. //按照商品id进行分流
  27. .keyBy("itemId")
  28. //滑动窗口,每隔5分钟,获得之前1小时窗口中的数据
  29. .timeWindow(Time.hours(1), Time.minutes(5))
  30. //预聚合,第一个参数:求和,第二个参数:每个商品在每个窗口的点击量
  31. .aggregate( new CountAgg(), new WindowResultFunction() )
  32. // 因为之前已经按商品id分流,这里再按窗口分流,
  33. // 所以会获得同一窗口的不同商品的数据,
  34. // 便于后面将同一窗口的不同商品的数据进行浏览次数统计,来获得最热门的商品
  35. .keyBy("windowEnd")
  36. //在自定义函数中进行业务逻辑处理
  37. .process( new TopNHotItems(2))
  38. .print()
  39. env.execute("Hot Items Job")
  40. }
  41. //统计出总数
  42. class CountAgg extends AggregateFunction[UserBehaviorL1, Long, Long]{
  43. override def add(value: UserBehaviorL1, accumulator: Long): Long = accumulator + 1
  44. override def createAccumulator(): Long = 0L
  45. override def getResult(accumulator: Long): Long = accumulator
  46. override def merge(a: Long, b: Long): Long = a + b
  47. }
  48. //获得某个商品在一个窗口中的总数
  49. class WindowResultFunction extends WindowFunction[Long, ItemViewCountL1, Tuple, TimeWindow]{
  50. override def apply(key: Tuple, window: TimeWindow, input: Iterable[Long], out: Collector[ItemViewCountL1]): Unit = {
  51. val itemId: Long = key.asInstanceOf[Tuple1[Long]].f0
  52. val count = input.iterator.next()
  53. out.collect(ItemViewCountL1(itemId, window.getEnd, count))
  54. }
  55. }
  56. //求某个窗口中前N名的热门点击商品
  57. class TopNHotItems(topSize: Int) extends KeyedProcessFunction[Tuple, ItemViewCountL1, String]{
  58. //ListState用于保存状态
  59. lazy val itemState: ListState[ItemViewCountL1] = getRuntimeContext
  60. .getListState( new ListStateDescriptor[ItemViewCountL1]( "itemState", classOf[ItemViewCountL1] ) )
  61. //注册定时器
  62. override def processElement(i: ItemViewCountL1, context: KeyedProcessFunction[Tuple, ItemViewCountL1, String]#Context, collector: Collector[String]): Unit = {
  63. //添加本窗口中的所有数据
  64. itemState.add(i)
  65. //windowEnd+1ms作为关闭窗口的触发时间
  66. context.timerService.registerEventTimeTimer( i.windowEnd + 1 )
  67. }
  68. //到了windowEnd+1时间后,执行onTimer方法
  69. override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[Tuple, ItemViewCountL1, String]#OnTimerContext, out: Collector[String]): Unit = {
  70. val allItems: ListBuffer[ItemViewCountL1] = ListBuffer()
  71. import scala.collection.JavaConversions._
  72. //将ListStae对象的数据存入到ListBuffer对象
  73. for(item <- itemState.get){
  74. allItems += item
  75. }
  76. //清除状态中的数据
  77. itemState.clear()
  78. //按count进行降序排序,获得前3名的数据
  79. val sortedItems = allItems.sortBy(_.count)(Ordering.Long.reverse).take(topSize)
  80. //格式化,打印
  81. val result: StringBuilder = new StringBuilder
  82. result.append("====================================\n")
  83. result.append("时间:").append(new Timestamp(timestamp - 1)).append("\n")
  84. for( i <- sortedItems.indices ){
  85. val currentItem: ItemViewCountL1 = sortedItems(i)
  86. result.append("No").append(i+1).append(":")
  87. .append(" 商品ID=").append(currentItem.itemId)
  88. .append(" 浏览量=").append(currentItem.count).append("\n")
  89. }
  90. result.append("====================================\n\n")
  91. out.collect(result.toString)
  92. // 终止程序,只输出一次
  93. sys.exit()
  94. //控制输出频率,模拟实时滚动
  95. // Thread.sleep(1000)
  96. }
  97. }
  98. }

编程要求

根据提示,在右侧编辑器补充代码,计算排行榜并实时输出,

要求每 30 秒计算之前 2 分钟的时间窗口数据, 对其中的浏览商品行为(即行为类型为 pv)进行浏览次数的计算,并输出浏览次数排名前 3 的商品结果。

数据格式如下表:

用户id商品id商品类目id行为类型时间戳
54346217151464116pv1511658000

以逗号进行分隔。

测试说明

平台会对你编写的代码进行测试:

测试输入:电商数据日志文件; 预期输出:

 
  1. ====================================
  2. 时间:2017-11-26 01:00:30.0
  3. No1: 商品ID=1715 浏览量=3
  4. No2: 商品ID=2244074 浏览量=2
  5. No3: 商品ID=5140156 浏览量=1
  6. ====================================
  7. ====================================
  8. 时间:2017-11-26 01:01:00.0
  9. No1: 商品ID=1715 浏览量=3
  10. No2: 商品ID=2244074 浏览量=2
  11. No3: 商品ID=5140156 浏览量=1
  12. ====================================
  13. ====================================
  14. 时间:2017-11-26 01:01:30.0
  15. No1: 商品ID=1715 浏览量=3
  16. No2: 商品ID=2244074 浏览量=2
  17. No3: 商品ID=5140156 浏览量=1
  18. ====================================
  19. ====================================
  20. 时间:2017-11-26 01:02:00.0
  21. No1: 商品ID=1715 浏览量=3
  22. No2: 商品ID=2244074 浏览量=2
  23. No3: 商品ID=5140156 浏览量=1
  24. ====================================

参考答案

import java.sql.Timestamp
import java.util.Properties

import org.apache.flink.api.common.functions.AggregateFunction
import org.apache.flink.api.common.state.{ListState, ListStateDescriptor}
import org.apache.flink.api.java.io.TextInputFormat
import org.apache.flink.api.java.tuple.{Tuple, Tuple1}
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.KeyedProcessFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.scala.function.WindowFunction
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
import org.apache.flink.util.Collector

import scala.collection.mutable.ListBuffer

//输入数据
case class UserBehaviorA1( userId: Long, itemId: Long, categoryId: Int, behavior: String, timestamp: Long )
//输出数据
case class ItemViewCountA1( itemId: Long, windowEnd: Long, count: Long )

object Test1 {
  def main(args: Array[String]): Unit = {

    val properties = new Properties()

    //flink的流执行环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment

    //设定Time类型为EventTime
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    //并发为1
    env.setParallelism(1)

    //获得数据源的文件
    val stream = env.readFile[String](
      new TextInputFormat(null),
      "/data/workspace/myshixun/pflinkhotitems4_1/src/UserBehavior1.csv")

      //获得文件中的每一行记录
      val stream1 = stream.map(line => {
      val linearray = line.split(",")
      UserBehaviorA1( linearray(0).toLong, linearray(1).toLong, linearray(2).toInt, linearray(3), linearray(4).toLong )
    })

      //指定时间戳
      .assignAscendingTimestamps(_.timestamp * 1000)
      //进行过滤,只获得pv行为的数据
      .filter(_.behavior == "pv")
      //按照商品id进行分流
      .keyBy("itemId")
      //滑动窗口,每隔30秒,获得之前2分钟时间窗口中的数据
      // 可以根据具体需求来设置此时间,
      // 比如:每隔5分钟获得之前1小时时间窗口中的数据的代码如下
      //  .timeWindow(Time.hours(1), Time.minutes(5))
      .timeWindow(Time.minutes(2), Time.seconds(30))
      //预聚合,第一个参数:求和,第二个参数:每个商品在每个窗口的点击量
      .aggregate( new CountAgg(), new WindowResultFunction() )
      // 因为之前已经按商品id分流,这里再按窗口分流,
      // 所以会获得同一窗口的不同商品的数据,
      // 便于后面将同一窗口的不同商品的数据进行浏览次数统计,来获得最热门的商品
      .keyBy("windowEnd")
      //在自定义函数中进行业务逻辑处理
      .process( new TopNHotItems(3))

    stream1.writeAsText("/root/files/result.txt").setParallelism(1)

    env.execute("计算排行榜并实时输出")
  }


  //统计出总数
  class CountAgg extends AggregateFunction[UserBehaviorA1, Long, Long]{
    override def add(value: UserBehaviorA1, accumulator: Long): Long = accumulator + 1

    override def createAccumulator(): Long = 0L

    override def getResult(accumulator: Long): Long = accumulator

    override def merge(a: Long, b: Long): Long = a + b
  }


  //获得某个商品在一个窗口中的总数
  class WindowResultFunction extends WindowFunction[Long, ItemViewCountA1, Tuple, TimeWindow]{
    override def apply(key: Tuple, window: TimeWindow, input: Iterable[Long], out: Collector[ItemViewCountA1]): Unit = {
      val itemId: Long = key.asInstanceOf[Tuple1[Long]].f0
      val count = input.iterator.next()
      out.collect(ItemViewCountA1(itemId, window.getEnd, count))
    }
  }


  //求某个窗口中前N名的热门点击商品
  class TopNHotItems(topSize: Int) extends KeyedProcessFunction[Tuple, ItemViewCountA1, String]{

    //ListState用于保存状态
    lazy val itemState: ListState[ItemViewCountA1] = getRuntimeContext
      .getListState( new ListStateDescriptor[ItemViewCountA1]( "itemState", classOf[ItemViewCountA1] ) )

    //注册定时器
    override def processElement(i: ItemViewCountA1, context: KeyedProcessFunction[Tuple, ItemViewCountA1, String]#Context, collector: Collector[String]): Unit = {
      //添加本窗口中的所有数据
      itemState.add(i)

      //windowEnd+1ms作为关闭窗口的触发时间
      context.timerService.registerEventTimeTimer( i.windowEnd + 1 )
    }


    //到了windowEnd+1时间后,执行onTimer方法
    override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[Tuple, ItemViewCountA1, String]#OnTimerContext, out: Collector[String]): Unit = {

      val allItems: ListBuffer[ItemViewCountA1] = ListBuffer()
      import  scala.collection.JavaConversions._

      //将ListStae对象的数据存入到ListBuffer对象
      for(item <- itemState.get){
        allItems += item
      }

      //清除状态中的数据
      itemState.clear()

      //********** Begin **********
      //按count进行降序排序,获得前3名的数据
      val sortedItems = allItems.sortBy(_.count)(Ordering.Long.reverse).take(topSize)





      //********** End **********

      //格式化,打印
      val result: StringBuilder = new StringBuilder
      result.append("====================================\n")
      result.append("时间:").append(new Timestamp(timestamp - 1)).append("\n")

      for( i <- sortedItems.indices ){
        val currentItem: ItemViewCountA1 = sortedItems(i)

        result.append("No").append(i+1).append(":")
          .append("  商品ID=").append(currentItem.itemId)
          .append("  浏览量=").append(currentItem.count).append("\n")
      }
      result.append("====================================\n\n")


      out.collect(result.toString)
      // 终止程序,只输出一次
      //sys.exit()

      //控制输出频率,实时滚动,会每隔1秒就输出一个结果
      Thread.sleep(1000)
    }
  }
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值