Flink_网站总浏览量(PV)的统计

1、PV(page view)

即页面浏览量;用户每1次对网站中的每个网页访问均被记录1次。用户对同一页面的多次访问,访问量累计。

2.UV(unique visitor) 网站独立访客数

指访问某个站点或点击某条新闻的不同IP地址的人数。

在同一天内,uv 只记录第一次进入网站的具有独立IP的访问者,在同一天内再次访问该网站则不计数。独立IP访问者提供了一定时间内不同观众数量的统计指标,而没有反应出网站的全面活动。

         衡量网站流量一个最简单的指标,就是网站的页面浏览量(Page View,PV)。 用户每次打开一个页面便记录 1 次 PV,多次打开同一页面则浏览量累计 。一般来说, PV 与来访者的数量成正比,但是 PV 并不直接决定页面的真实来访者数量,如同一个来访者通过不断的刷新页面,也可以制造出非常高的 PV。
        我们知道,用户浏览页面时,会从浏览器向网络服务器发出一个请求(Request),网络服务器接到这个请求后,会将该请求对应的一个网页(Page )发送给浏览器, 从而产生了一个 PV 。所以我们的统计方法, 可以是从 web 服务器的日志中去提取对应的页面访问然后统计,就向上一节中的做法一样;也可以直接从埋点日志中提 取用户发来的页面请求,从而统计出总浏览量。

 

输入数据:

import org.apache.flink.api.common.functions.{AggregateFunction, MapFunction}
import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor}
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.util.Random


// 定义输入样例类
case class UserBehavior(userId: Long,
                        itemId: Long,
                        categoryId: Int,
                        behavior: String,
                        timestamp: Long )
// 网站总浏览量
object PageView {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //env.setParallelism(1)
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

    val inputStream: DataStream[String] = env.readTextFile("D:\\Mywork\\workspace\\Project_idea\\UserBehaviorAnalysis0903\\HotItemsAnalysis\\src\\main\\resources\\UserBehavior.csv")
    //转换成样例类类型,并提取时间戳
    val dataStream = inputStream.map(data => {
      val arr: Array[String] = data.split(",")
      UserBehavior(arr(0).toLong, arr(1).toLong, arr(2).toInt, arr(3), arr(4).toLong)
    }).assignAscendingTimestamps(_.timestamp * 1000L)

    val pvStream = dataStream
      .filter(_.behavior == "pv")
      //      .map {data => ("pv", 1L) } //定义一个Pv字符串作为Dummy key(哑key)
      .map(new MyMapper())
      .keyBy(_._1) //所有数据分到同一个组
      .timeWindow(Time.hours(1)) //1H滚动窗口
      .aggregate(new PvCountAgg(), new PvCountWindowResult())

    val totalPvStream = pvStream
      .keyBy(_.windowEnd)
      .process(new TotalPvCountResult())

    totalPvStream.print()
    env.execute()
  }

}
//自定义Mapper,随机生成分组的Key
class MyMapper() extends MapFunction[UserBehavior, (String, Long)] {
  override def map(value: UserBehavior): (String, Long) = {
    (Random.nextString(10), 1L)
  }
}

//自定义预聚合函数
class PvCountAgg() extends AggregateFunction[(String, Long), Long, Long]{
  override def createAccumulator(): Long = 0L

  override def add(value: (String, Long), accumulator: Long): Long = accumulator + 1

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

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

//定义输出PV统计的样例类
case class PvCount(windowEnd: Long, count: Long)

//自定义窗口函数
class PvCountWindowResult() extends WindowFunction[Long, PvCount, String, TimeWindow]{
  override def apply(key: String, window: TimeWindow, input: Iterable[Long], out: Collector[PvCount]): Unit = {
    out.collect(PvCount(window.getEnd, input.head))
  }
}

class TotalPvCountResult() extends KeyedProcessFunction[Long, PvCount, PvCount] {
  lazy val totalPvCountResultState: ValueState[Long]= getRuntimeContext.getState(new ValueStateDescriptor[Long]("total-Pv", classOf[Long]))

  override def processElement(value: PvCount, ctx: KeyedProcessFunction[Long, PvCount, PvCount]#Context, out: Collector[PvCount]): Unit = {
    totalPvCountResultState.update(totalPvCountResultState.value() + value.count)
    //注册一个Windowed+1S后触发定时器
    ctx.timerService().registerEventTimeTimer(value.windowEnd + 1)
  }

  override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[Long, PvCount, PvCount]#OnTimerContext, out: Collector[PvCount]): Unit = {
    val totalPvCount = totalPvCountResultState.value()
    out.collect(PvCount(ctx.getCurrentKey, totalPvCount)) //(windowEnd: Long, count: Long)
    totalPvCountResultState.clear()
  }
}

输出结果:

 12> PvCount(1511668800000,47298)
7> PvCount(1511665200000,48022)
6> PvCount(1511679600000,50838)
10> PvCount(1511672400000,44499)
1> PvCount(1511676000000,48649)
11> PvCount(1511683200000,52296)
7> PvCount(1511694000000,13)
4> PvCount(1511661600000,41890)
4> PvCount(1511686800000,52552)
4> PvCount(1511690400000,48292)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值