Flink基于State做千万用户的pv

需求:记录每天某一页面下所有用户的访问次数和第一次访问的时间
解法:

  1. redis做缓存,每天一个map,设置ttl,用户访问次数做累积,过滤完先存到redis,sink的时候读redis,查出这个用户的总访问次数
  2. 用flink的keyby(user_id+date),生成count和min,使用checkpoint进行容错

    对于上边两种做法各有缺陷,第一种需要借助外部存储,任务出问题的时候重启无法保证累计不重复更新;第二种需要会占用大量的内存,无法清理过期的user_id+date,没几天就oom。
    此时我想到第一种解决方案,用keyby(user_id).process来解决,对每个user_id生成一个管道,用ValueState进行管理,管理两个值(count, min_time),然后判断日志的时间来确定是那一天,然后用state的ttl来解决占有内存的问题,但是后来使用ttl的时候就把我劝退了,state的ttl是惰性删除,过期了不访问就不会删除,那我还要他有个屁用。
    于是想到第二种方案,用keyby(user_id%1000).process来解决,保证用户会分到同一个桶里,维护一个MapState[date_time, Map(user, (count, min_time))],定期删除过期的date_time,一是就解决了ttl的问题,二是保证每次都能删除昨天的分区,三是分多个桶也会保证数据不倾斜。另外还有个优点,从checkponit中恢复的时候不会有重复累计count,保证数据的准确性,用的是FsStateBackend。

核心代码如下


env.setStateBackend(new FsStateBackend(s"hdfs:///flink/checkpoint/xxx/$jobName"));

map((user_id%1000, user_id, timestamp))
.keyby(_._1)
.process(new MonitorKeyedProcessFunction)


class MonitorKeyedProcessFunction() extends KeyedProcessFunction[Long, (Long, String, String, String), JSONObject] {

    private var state: MapState[String, java.util.HashMap[String, (Int, String)]] = _

    override def open(parameters: Configuration): Unit = {
      // 创建 ValueStateDescriptor
      val descriptor = new MapStateDescriptor[String, java.util.HashMap[String, (Int, String)]]("myState", classOf[String], classOf[java.util.HashMap[String, (Int, String)]])

      // 基于 ValueStateDescriptor 创建 ValueState
      state = getRuntimeContext.getMapState(descriptor)

    }

    override def processElement(value: (Long, String, String, String),
                                context: KeyedProcessFunction[Long, (Long, String, String, String), JSONObject]#Context,
                                out: Collector[JSONObject]): Unit = {

      val user_id = value._2
      val time = value._3
      val date = DateTimeUtil.tranTimeToString(time, "yyyy-MM-dd")
      var current = state.get(date)
      // 总数
      var count = 0
      // 第一次访问时间
      var first_modified = time

      // 初始化
      if (current == null) {
        current = new java.util.HashMap[String, (Int, String)]
      }
      if (current.keys.contains(user_id)) {
        val info = current(user_id)
        count = info._1
        first_modified = info._2
      }
      // 最小时间
      if (time <  first_modified)
        first_modified = time
      // 累加
      count += 1
      // 提交
      current.put(user_id, (count, first_modified))
      // 更新
      state.put(date, current)
      
      // 删除过期日期数据 1点左右
      val yesterday = System.currentTimeMillis()-(86400+3600)*1000
      val yesterdayStr = DateTimeUtil.tranTimeToString(yesterday, "yyyy-MM-dd")
      if(state.get(yesterdayStr)!=null){
        if(state.get(yesterdayStr).size()!=0){
          state.remove(yesterdayStr)
          println(s"删除此分区${yesterdayStr}的状态数据")
        }
      }
      // 输出
      val res = new JSONObject()
      res.put("user_id", user_id)
      res.put("count", count)
      res.put("min_time", first_modified)
      out.collect(res)
    }
  }
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在这个科技高速发展的时代,经历了PC时代几乎人手一台电脑,随之衍生出站长这个概念;移动互联网时代几乎人手一部智能手机,智能手机一般都会安装很多应用,目前应用呈爆发式的增长;随着产业的不断深入发展,小程序的发展也日益壮大,应用涵盖各个领域;如今一个公司就可能有多个软件应用,对于软件开发商来说,急需一套分析系统帮助软件运营,如果单独开发一个分析系统去针对一个软件进行分析的话,成本会非常的大,这个成本包含开发成本以及以后的维护成本。为了解决了上述的问题,我们开发出了一套云产品:亿级动态数据统计分析系统,本系统可以支持所有的终端  (Web端、移动端、小程序端等 )数据统计,只要简单的使用sdk就可以接入我们的系统,软件开发商可以很轻松的对软件使用的情况进行监控,及时辅助公司对该软件的运营。该产品历经2年的实践,商业价值极高。本套案例是完全基于真实的产品进行开发和讲解的,同时对架构进行全面的升级,采用了全新的 Flink 架构+Node.js+Vue.js等,完全符合目前企业级的使用标准。对于本套课程在企业级应用的问题,可以提供全面的指导。Flink作为第四代大数据计算引擎,越来越多的企业在往Flink转换。Flink在功能性、容错性、性能方面都远远超过其他计算框架,兼顾高吞吐和低延时。Flink能够基于同一个Flink运行时,提供支持流处理和批处理两种类型应用的功能。也就是说同时支持流处理和批处理。Flink将流处理和批处理统一起来,也就是说作为流处理看待时输入数据流是无界的;批处理被作为一种特殊的流处理,只是它的输入数据流被定义为有界的。Flink技术特点1. 流处理特性支持高吞吐、低延迟、高性能的流处理支持带有事件时间的窗口(Window)操作支持有状态计算的Exactly-once语义支持高度灵活的窗口(Window)操作,支持基于time、count、session,以及data-driven的窗口操作支持具有Backpressure功能的持续流模型支持基于轻量级分布式快照(Snapshot)实现的容错一个运行时同时支持Batch on Streaming处理和Streaming处理Flink在JVM内部实现了自己的内存管理支持迭代计算支持程序自动优化:避免特定情况下Shuffle、排序等昂贵操作,中间结果有必要进行缓存2. API支持对Streaming数据类应用,提供DataStream API对批处理类应用,提供DataSet API(支持Java/Scala)3. Libraries支持支持机器学习(FlinkML)支持图分析(Gelly)支持关系数据处理(Table)支持复杂事件处理(CEP)4. 整合支持支持Flink on YARN支持HDFS支持来自Kafka的输入数据支持Apache HBase支持Hadoop程序支持Tachyon支持ElasticSearch支持RabbitMQ支持Apache Storm支持S3支持XtreemFS课程所涵盖的知识点包括:Flink、 Node.js、 Vue.js、 Kafka、Flume、Spring、SpringMVC、Dubbo、HDFS、Hbase、Highcharts等等  企业一线架构师讲授,代码在老师指导下可以复用,提供企业解决方案。  版权归作者所有,盗版将进行法律维权。   
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值