• Flink模拟项目: 实时流量统计


    3.1 模块创建和数据准备

    在UserBehaviorAnalysis下新建一个 maven module作为子项目,命名为NetworkTrafficAnalysis。在这个子模块中,我们同样并没有引入更多的依赖,所以也不需要改动pom文件。

    在src/main/目录下,将默认源文件目录java改名为scala。将apache服务器的日志文件apache.log复制到资源文件目录src/main/resources下,我们将从这里读取数据

    3.2 代码实现

    我们现在要实现的模块是 “实时流量统计”。对于一个电商平台而言,用户登录的入口流量、不同页面的访问流量都是值得分析的重要数据,而这些数据,可以简单地从web服务器的日志中提取出来。我们在这里实现最基本的“页面浏览数”的统计,也就是读取服务器日志中的每一行log,统计在一段时间内用户访问url的次数。

    具体做法为:每隔5秒,输出最近10分钟内访问量最多的前N个URL。可以看出,这个需求与之前“实时热门商品统计”非常类似,所以我们完全可以借鉴此前的代码。

    在src/main/scala下创建TrafficAnalysis.scala文件,新建一个单例对象。定义样例类ApacheLogEvent,这是输入的日志数据流;另外还有UrlViewCount,这是窗口操作统计的输出数据类型。在main函数中创建StreamExecutionEnvironment 并做配置,然后从apache.log文件中读取数据,并包装成ApacheLogEvent类型。

    需要注意的是,原始日志中的时间是“dd/MM/yyyy:HH:mm:ss”的形式,需要定义一个DateTimeFormat将其转换为我们需要的时间戳格式:

    .map(line => {
    val linearray = line.split(" ")
    val sdf = new SimpleDateFormat("dd/MM/yyyy:HH:mm:ss")
    val timestamp = sdf.parse(linearray(3)).getTime
    ApacheLogEvent(linearray(0), linearray(2), timestamp, 
    linearray(5), linearray(6))
    })
    

      

    完整代码如下:

     

    case class ApacheLogEvent(ip: String, userId: String, eventTime: Long, method: String, url: String)
    case class UrlViewCount(url: String, windowEnd: Long, count: Long)
    
    object TrafficAnalysis {
    
      def main(args: Array[String]): Unit = {
        val env = StreamExecutionEnvironment.getExecutionEnvironment
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
        env.setParallelism(1)
        val stream = env
    // 以window下为例,需替换成自己的路径
          .readTextFile("YOUR_PATH\resources\apache.log")
          .map(line => {
    val linearray = line.split(" ")
    val simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy:HH:mm:ss")
    val timestamp = simpleDateFormat.parse(linearray(3)).getTime
            ApacheLogEvent(linearray(0), linearray(2), timestamp, linearray(5), linearray(6))
          })
          .assignTimestampsAndWatermarks(new 
    BoundedOutOfOrdernessTimestampExtractor[ApacheLogEvent]
    (Time.milliseconds(1000)) {
    override def extractTimestamp(t: ApacheLogEvent): Long = {
    t.eventTime
    }
    })
          .keyBy("url")
          .timeWindow(Time.minutes(10), Time.seconds(5))
          .aggregate(new CountAgg(), new WindowResultFunction())
          .keyBy(1)
          .process(new TopNHotUrls(5))
          .print()
        env.execute("Traffic Analysis Job")
      }
    
      class CountAgg extends AggregateFunction[ApacheLogEvent, Long, Long] {
        override def createAccumulator(): Long = 0L
        override def add(apacheLogEvent: ApacheLogEvent, acc: Long): Long = acc + 1
        override def getResult(acc: Long): Long = acc
        override def merge(acc1: Long, acc2: Long): Long = acc1 + acc2
      }
    
      class WindowResultFunction extends WindowFunction[Long, UrlViewCount, Tuple, TimeWindow] {
        override def apply(key: Tuple, window: TimeWindow, aggregateResult: Iterable[Long], collector: Collector[UrlViewCount]) : Unit = {
          val url: String = key.asInstanceOf[Tuple1[String]].f0
          val count = aggregateResult.iterator.next
          collector.collect(UrlViewCount(url, window.getEnd, count))
        }
      }
    
      class TopNHotUrls(topsize: Int) extends KeyedProcessFunction[Tuple, UrlViewCount, String] {
        private var urlState : ListState[UrlViewCount] = _
    
        override def open(parameters: Configuration): Unit = {
          super.open(parameters)
          val urlStateDesc = new ListStateDescriptor[UrlViewCount]("urlState-state", classOf[UrlViewCount])
          urlState = getRuntimeContext.getListState(urlStateDesc)
        }
    
        override def processElement(input: UrlViewCount, context: KeyedProcessFunction[Tuple, UrlViewCount, String]#Context, collector: Collector[String]): Unit = { 
    // 每条数据都保存到状态中
          urlState.add(input)
          context.timerService.registerEventTimeTimer(input.windowEnd + 1)
        }
    
        override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[Tuple, UrlViewCount, String]#OnTimerContext, out: Collector[String]): Unit = { 
    // 获取收到的所有URL访问量
          val allUrlViews: ListBuffer[UrlViewCount] = ListBuffer()
          import scala.collection.JavaConversions._
          for (urlView <- urlState.get) {
            allUrlViews += urlView
          }
          // 提前清除状态中的数据,释放空间
          urlState.clear()
          // 按照访问量从大到小排序
          val sortedUrlViews = allUrlViews.sortBy(_.count)(Ordering.Long.reverse)
    .take(topSize)
          // 将排名信息格式化成 String, 便于打印
          var result: StringBuilder = new StringBuilder
          result.append("====================================
    ")
          result.append("时间: ").append(new Timestamp(timestamp - 1)).append("
    ")
    
          for (i <- sortedUrlViews.indices) {
            val currentUrlView: UrlViewCount = sortedUrlViews(i)
    // e.g.  No1:  URL=/blog/tags/firefox?flav=rss20  流量=55
            result.append("No").append(i+1).append(":")
    .append("  URL=").append(currentUrlView.url)
    .append("  流量=").append(currentUrlView.count).append("
    ")
          }
          result.append("====================================
    
    ")
          // 控制输出频率,模拟实时滚动结果
          Thread.sleep(1000)
          out.collect(result.toString)
        }
      }
    }
    

      

     

  • 相关阅读:
    localhost和本机IP和127.0.0.1之间的区别
    git客户端msysGit和TortoiseGit使用
    JS正则
    css中外边距
    css定位浮动总结
    Teleport Ultra 抓包工具
    编程实践心得与设计思想
    Java 读写Properties配置文件
    如何成为一个优秀的DBA
    对DB2常见错误的列举以及破解方案
  • 原文地址:https://www.cnblogs.com/tesla-turing/p/13276480.html
Copyright © 2020-2023  润新知