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

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

相关知识

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

计算排行榜并实时输出

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

编程要求

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

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

数据格式如下表:

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

以逗号进行分隔。

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 **********


      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)
    }
  }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值