Flink学习(十五) 滑动事件时间窗口加上水位线开始窗口时间如何确定?(底层源码)

先看上一节的代码程序

package com.wyh.windowsApi

import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.functions.{AssignerWithPeriodicWatermarks, AssignerWithPunctuatedWatermarks}
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.watermark.Watermark
import org.apache.flink.streaming.api.windowing.assigners.SlidingEventTimeWindows
import org.apache.flink.streaming.api.windowing.time.Time


object WindowTest {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment

    env.setParallelism(1)
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    //周期性生成watermark 默认是200毫秒
    env.getConfig.setAutoWatermarkInterval(100L)

    /**
      * 从文件中读取数据
      *
      *
      */
    //val stream = env.readTextFile("F:\\flink-study\\wyhFlinkSD\\data\\sensor.txt")

    val stream = env.socketTextStream("localhost", 7777)

    //Transform操作
    val dataStream: DataStream[SensorReading] = stream.map(data => {
      val dataArray = data.split(",")
      SensorReading(dataArray(0).trim, dataArray(1).trim.toLong, dataArray(2).trim.toDouble)
    })
      //===到来的数据是升序的,准时发车,用assignAscendingTimestamps
      //指定哪个字段是时间戳 需要的是毫秒 * 1000
      //      .assignAscendingTimestamps(_.timestamp * 1000)
      //===处理乱序数据
      //      .assignTimestampsAndWatermarks(new MyAssignerPeriodic())
      //==底层也是周期性生成的一个方法 处理乱序数据 延迟1秒种生成水位 同时分配水位和时间戳 括号里传的是等待延迟的时间
      .assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[SensorReading](Time.seconds(1)) {
      override def extractTimestamp(t: SensorReading): Long = {
        t.timestamp * 1000
      }
    })

    //统计10秒内的最小温度
    val minTemPerWindowStream = dataStream
      .map(data => (data.id, data.temperature))
      .keyBy(0)
      //      .timeWindow(Time.seconds(10)) //开时间窗口  滚动窗口 没有数据的窗口不会触发
      //左闭右开 包含开始 不包含结束 延迟1秒触发的那个时间的数据不包含
      //可以直接调用底层方法,第三个参数传offset代表时区
      //.window(SlidingEventTimeWindows.of(Time.seconds(15),Time.seconds(5),Time.hours(-8)))
      .timeWindow(Time.seconds(15), Time.seconds(5)) //滑动窗口,每隔5秒输出一次
      .reduce((data1, data2) => (data1._1, data1._2.min(data2._2))) //用reduce做增量聚合


    minTemPerWindowStream.print("min temp")

    dataStream.print("input data")

    env.execute("window Test")

  }

}


//设置水位线(水印) 这里有两种方式实现
//一种是周期性生成 一种是以数据的某种特性进行生成水位线(水印)
/**
  * 周期性生成watermark 默认200毫秒
  */
class MyAssignerPeriodic() extends AssignerWithPeriodicWatermarks[SensorReading] {
  val bound: Long = 60 * 1000
  var maxTs: Long = Long.MaxValue

  override def getCurrentWatermark: Watermark = {
    //定义一个规则进行生成
    new Watermark(maxTs - bound)
  }

  //用什么抽取这个时间戳
  override def extractTimestamp(t: SensorReading, l: Long): Long = {
    //保存当前最大的时间戳
    maxTs = maxTs.max(t.timestamp)
    t.timestamp * 1000
  }
}


/**
  * 乱序生成watermark
  * 每来一条数据就生成一个watermark
  */
class MyAssignerPunctuated() extends AssignerWithPunctuatedWatermarks[SensorReading] {
  override def checkAndGetNextWatermark(t: SensorReading, l: Long): Watermark = {
    new Watermark(l)
  }

  override def extractTimestamp(t: SensorReading, l: Long): Long = {
    t.timestamp * 1000
  }
}

开始点源码 Ctrl + 鼠标左键

 

 点进去发现是KededStream里面的其中一个方法,继续点

 

 我们发现实际上是封装了一层java代码,代码中TimeWindow本身就是一个简写,这里发现底层还是.window() 方法 传入窗口类型参数

 

 我们发现,如果窗口的时间是处理时间就调用滑动处理时间窗口,我们在代码中设置了事件时间,

env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

所以这里是滑动事件处理时间窗口。

继续点

 

 点进去我们就看到实现的方法了

 

 先来看最开始的时间是如何生成的,继续点

 

 我们就看到这样的一个计算公式

 

 来解释一下,我们可以看到这样一个参数,offset,它如果我们没有设置就默认为0。它本身是用来指定时间的时区的。注意:这里有个参数其实叫windowSize 其实传进来的是一个滑动步长!!!但是不影响结果

如何在代码中添加这个offset呢:.window() 方法中传入

SlidingEventTimeWindows.of()  第三个参数就是offset
.window(SlidingEventTimeWindows.of(Time.seconds(15),Time.seconds(5),Time.hours(-8)))

在这里,就计算出第一个窗口开始时间。

继续看调用的

我们可以看到,一个for循环追加了好多窗口window.

判断如果开始时间大于时间戳减去窗口的大小,那么就把当前这个窗口加上一个创建口大小,然后再减去一个滑动步长,再判断是否大于时间戳减去窗口的大小,以此类推,知道小于,就结束创建,就可以得出最早创建的窗口。

 

 

如果是滚动窗口,传进来的就是最早结束的时间,直接加上窗口大小

 

posted @ 2020-05-23 17:02  Xiaohu_BigData  阅读(2300)  评论(0编辑  收藏  举报