Flink 之Window(窗口)

关窗时间

 

情况1:如果存在lateness延迟关窗设置,则
        
            关窗时间:最后一条数据event_time - watermark时间 - lateness时间
            窗口开始计算时间: 当时数据event_time - watermark时间

如:latenes:为10分钟;窗口设置为:10分钟,步长为5;watermark:为1s

则数据在10:50:00秒开始到来,则这条数据在【10:50:00 10:59:59)这个窗口都是有效,但是在10:50:52数据时刻开始窗口得计算

如果数据是10:49:59得数据,它在
【10:50:00 10:59:59)这个最后窗口关闭时都没有到来,就会输出到侧输出流

 

知识点:

注意 window () 方法必须在 keyBy 之后才能用

window 类型 
    • 时间窗口(Time Window) 
        滚动时间窗口(tumbling window):将数据依据固定的窗口长度对数据进行切分 ;时间对齐,窗口长度固定,没有重叠
            适用场景:适合做 BI 统计等(做每个时间段的聚合计算)
        滑动时间窗口 (sliding window):滑动窗口是固定窗口的更广义的一种形式,滑动窗口由固定的窗口 长度和滑动间隔组成 ; 窗口长度固定,可以有重叠
            适用场景:对最近一个时间段内的统计(求某接口最近 5min 的失败率来决定是 否要报警)。
        会话窗口 (session window):由一系列事件组合一个指定时间长度的 timeout 间隙组成,也就是 一段时间没有接收到新数据就会生成新的窗口 ;时间无对齐
    • 计数窗口(Count Window) 
        滚动计数窗口 
        滑动计数窗口
        
window function :定义了要对窗口中收集的数据做的计算操作 
    • 可以分为两类  
        增量聚合函数(incremental aggregation functions) : 每条数据到来就进行计算,保持一个简单的状态 ; ReduceFunction, AggregateFunction  
     全窗口函数(full window functions) : 先把窗口所有数据收集起来,等到计算的时候会遍历所有数据 ; ProcessWindowFunction Window其他Api .trigger() —— 触发器 : 定义 window 什么时候关闭,触发计算并输出结果 .evictor() —— 移除器 : 定义移除某些数据的逻辑 .allowedLateness() —— 允许处理迟到的数据 .sideOutputLateData() —— 将迟到的数据放入侧输出流 .getSideOutput() —— 获取侧输出流

1、代码案例1

package window

import com.yangwj.api.SensorReading
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time
/**
 * @author yangwj
 * @date 2021/1/7 21:45
 * @version 1.0
 */
object WindowTest {
  /**
   * 窗口:分为时间窗口和计数窗口
   * @param args
   */
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    val inputFile:String = "G:\\Java\\Flink\\guigu\\flink\\src\\main\\resources\\sensor.txt"
    val input: DataStream[String] = env.readTextFile(inputFile)

    val dataStream = input.map(data => {
      val arr: Array[String] = data.split(",")
      SensorReading(arr(0), arr(1).toLong, arr(2).toDouble)
    })

    dataStream
      .map(data => (data.id,data.temperature))
      .keyBy(_._1) //按照二元组的第一个元素分组
     // .window(TumblingEventTimeWindows.of(Time.seconds(15))) //定义滚动时间窗口
//      .window(SlidingEventTimeWindows.of(Time.minutes(5),Time.seconds(5)))//滑动窗口
//      .window(EventTimeSessionWindows.withGap(Time.seconds(5)))// 会话窗口
      .timeWindow(Time.seconds(15))//定义滚动时间窗口
//      .timeWindow(Time.minutes(5),Time.seconds(5))//滑动窗口
      //.countWindow(5)//计数窗口
      .reduce((r1,r2) => (r1._1,r1._2.min(r2._2))).print("test")
    
    env.execute(" window test")
  }
}

2、代码案例2

package window

import com.yangwj.api.SensorReading
import org.apache.flink.streaming.api.functions.source.SourceFunction
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment, _}
import org.apache.flink.streaming.api.windowing.time.Time

import scala.util.Random

/**
 * @author yangwj
 * @date 2021/1/7 21:45
 * @version 1.0
 */
object WindowTest1 {
  /**
   * 窗口:分为时间窗口和计数窗口
   * @param args
   */
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment

    val dataStream: DataStream[SensorReading] = env.addSource(new MySensorSource)
    //每15秒统计一次温度最小值
    val value: DataStream[(String, Double, Long)] = dataStream
      .map(data => (data.id, data.temperature, data.timestamp))
      .keyBy(_._1) //按照二元组的第一个元素分组
      // .window(TumblingEventTimeWindows.of(Time.seconds(15))) //定义滚动时间窗口
      //      .window(SlidingEventTimeWindows.of(Time.minutes(5),Time.seconds(5)))//滑动窗口
      //      .window(EventTimeSessionWindows.withGap(Time.seconds(5)))// 会话窗口
      .timeWindow(Time.seconds(15)) //定义滚动时间窗口
      //      .timeWindow(Time.minutes(5),Time.seconds(5))//滑动窗口
      //.countWindow(5)//计数窗口
      //      .minBy(1)
      .reduce((curs, newd) => (curs._1, curs._2.min(newd._2), newd._3))

    value.print("windows")
    env.execute("reduce test")

  }
}
//自定义数据源
class MySensorSource extends SourceFunction[SensorReading]{

  //定义一个标识位,用来表示数据源是否正常运行发出数据
  var running :Boolean = true
  //sourceContext 发送数据
  override def run(sourceContext: SourceFunction.SourceContext[SensorReading]): Unit = {
    //定义无线循环,不断产生数据,除非被cancel
    val rand = new Random()
    var curTemp= 1.to(4).map(i => ("sensor" + i, rand.nextDouble() * 100))

    while (running){
      curTemp = curTemp.map(data =>(data._1,data._2+rand.nextGaussian()))

      val curTime = System.currentTimeMillis()
      println("输入值:"+curTemp+",时间为:"+curTime)
      curTemp.foreach(data => sourceContext.collect(SensorReading(data._1,curTime,data._2)))
      Thread.sleep(3000)
    }
  }

  override def cancel(): Unit = false
}

 

posted @ 2021-01-09 10:49  小白啊小白,Fighting  阅读(395)  评论(0编辑  收藏  举报