Flink-Window概述 | Window類型 | TimeWindow、CountWindow、SessionWindow、WindowFunction

>代碼GitHub:https://github.com/SmallScorpion/flink-tutorial.git

Window概述

  1. streaming流式計算是一種被設計用於處理無限數據集的數據處理引擎,而無限數據集是指一種不斷增長的本質上無限的數據集,而window是一種切割無限數據爲有限塊進行處理的手段。
  2. Window是無限數據流處理的核心,Window將一個無限的stream拆分成有限大小的”buckets”桶,我們可以在這些桶上做計算操作。
    在這裏插入圖片描述

Window類型

1. CountWindow:按照指定的數據條數生成一個Window,與時間無關。
2. TimeWindow:按照時間生成Window。

在這裏插入圖片描述

滾動窗口(Tumbling Windows)

將數據依據固定的窗口長度對數據進行切片。
特點:時間對齊,窗口長度固定,沒有重疊。

滾動窗口分配器將每個元素分配到一個指定窗口大小的窗口中,滾動窗口有一個固定的大小,並且不會出現重疊。例如:如果你指定了一個5分鐘大小的滾動窗口,窗口的創建如下圖所示:
在這裏插入圖片描述

滑動窗口(Sliding Windows)

滑動窗口是固定窗口的更廣義的一種形式,滑動窗口由固定的窗口長度和滑動間隔組成。
特點:時間對齊,窗口長度固定,可以有重疊。

滑動窗口分配器將元素分配到固定長度的窗口中,與滾動窗口類似,窗口的大小由窗口大小參數來配置,另一個窗口滑動參數控制滑動窗口開始的頻率。因此,滑動窗口如果滑動參數小於窗口大小的話,窗口是可以重疊的,在這種情況下元素會被分配到多個窗口中。
例如,你有10分鐘的窗口和5分鐘的滑動,那麼每個窗口中5分鐘的窗口裏包含着上個10分鐘產生的數據,如下圖所示:

在這裏插入圖片描述

會話窗口(Session Windows)

由一系列事件組合一個指定時間長度的timeout間隙組成,類似於web應用的session,也就是一段時間沒有接收到新數據就會生成新的窗口。
特點:時間無對齊。

session窗口分配器通過session活動來對元素進行分組,session窗口跟滾動窗口和滑動窗口相比,不會有重疊和固定的開始時間和結束時間的情況,相反,當它在一個固定的時間週期內不再收到元素,即非活動間隔產生,那個這個窗口就會關閉。一個session窗口通過一個session間隔來配置,這個session間隔定義了非活躍週期的長度,當這個非活躍週期產生,那麼當前的session將關閉並且後續的元素將被分配到新的session窗口中去。
在這裏插入圖片描述

TimeWindow代碼實操

Flink默認的時間窗口根據Processing Time 進行窗口的劃分,將Flink獲取到的數據根據進入Flink的時間劃分到不同的窗口中。必須在KeyBy之後做窗口操作

import com.atguigu.bean.SensorReading
import org.apache.flink.api.common.functions.ReduceFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time

object TimeWindowTest {
  def main(args: Array[String]): Unit = {

    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment

    val inputDStream: DataStream[String] = env.socketTextStream("hadoop102", 7777)

    val dataDstream: DataStream[SensorReading] = inputDStream.map(
      data => {
        val dataArray: Array[String] = data.split(",")
        SensorReading(dataArray(0), dataArray(1).toLong, dataArray(2).toDouble)
      }
    )

    val windowDStream: DataStream[SensorReading] = dataDstream
      .keyBy(0)
      .timeWindow(Time.seconds(15))
      // .timeWindow(Time.seconds(15),Time.seconds(5)) 滑動窗口,第二個參數爲滑動步長
      .reduce( MyReduceFunc() )
	  //  .reduce((r1, r2) => (r1._1, r1._2.min(r2._2))) // 也可以直接寫,這裏是自定義
    
    dataDstream.print("data")
    windowDStream.print("window")

    env.execute("window test job")
  }
}

case class MyReduceFunc() extends ReduceFunction[SensorReading]{
  override def reduce(t: SensorReading, t1: SensorReading): SensorReading = {
    SensorReading(t.id, t1.timestamp, t.temperature.min(t1.temperature))
  }
}

在這裏插入圖片描述

CountWindow

默認的CountWindow是一個滾動窗口,只需要指定窗口大小即可,當元素數量達到窗口大小時,就會觸發窗口的執行。

import com.atguigu.bean.SensorReading
import org.apache.flink.streaming.api.scala._


object CountWindowTest {
  def main(args: Array[String]): Unit = {

    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment

    val inputDStream: DataStream[String] = env.readTextFile("D:\\MyWork\\WorkSpaceIDEA\\flink-tutorial\\src\\main\\resources\\SensorReading.txt")

    val dataDstream: DataStream[SensorReading] = inputDStream.map(
      data => {
        val dataArray: Array[String] = data.split(",")
        SensorReading(dataArray(0), dataArray(1).toLong, dataArray(2).toDouble)
      }
    )

    val windowDStream: DataStream[SensorReading] = dataDstream
      .keyBy(0)
      .countWindow(2) // 滾動窗口
      // 滑動窗口:sliding_size設置爲了2,也就是說,每收到兩個相同key的數據就計算一次,每一次計算的window範圍是10個元素。
      // .countWindow(10,2)
      //.reduce( MyReduceFunc() )
      .reduce((r1,r2) => SensorReading(r1.id, r1.timestamp, r1.temperature.min(r2.temperature)))


    dataDstream.print("data")
    windowDStream.print("window")

    env.execute("window test job")
  }
}

在這裏插入圖片描述

SessionWindow

時間窗口和計數窗口的底層都是.window()

import com.atguigu.bean.SensorReading
import org.apache.flink.streaming.api.scala._


object CountWindowTest {
  def main(args: Array[String]): Unit = {

    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment

    val inputDStream: DataStream[String] = env.readTextFile("D:\\MyWork\\WorkSpaceIDEA\\flink-tutorial\\src\\main\\resources\\SensorReading.txt")

    val dataDstream: DataStream[SensorReading] = inputDStream.map(
      data => {
        val dataArray: Array[String] = data.split(",")
        SensorReading(dataArray(0), dataArray(1).toLong, dataArray(2).toDouble)
      }
    )

    val windowDStream: DataStream[SensorReading] = dataDstream
      .keyBy(0)
      .countWindow(2) // 滾動窗口
      // 滑動窗口:sliding_size設置爲了2,也就是說,每收到兩個相同key的數據就計算一次,每一次計算的window範圍是10個元素。
      // .countWindow(10,2)
      //.reduce( MyReduceFunc() )
      .reduce((r1,r2) => SensorReading(r1.id, r1.timestamp, r1.temperature.min(r2.temperature)))

    dataDstream.print("data")
    windowDStream.print("window")

    env.execute("window test job")
  }
}

在這裏插入圖片描述

窗口函數(window function)

增量聚合函數只有在一個窗口關閉時,纔會將窗口內的數據操作如求最小值,最大值的數據進行輸出
在這裏插入圖片描述

Window和Aggregate簡單小需求

import com.atguigu.bean.SensorReading
import org.apache.flink.api.common.functions.{AggregateFunction, ReduceFunction}
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time

object AggregateForWindowTest {
  def main(args: Array[String]): Unit = {

    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment

    val inputDStream: DataStream[String] = env.socketTextStream("hadoop102", 7777)

    val dataDstream: DataStream[SensorReading] = inputDStream.map(
      data => {
        val dataArray: Array[String] = data.split(",")
        SensorReading(dataArray(0), dataArray(1).toLong, dataArray(2).toDouble)
      }
    )

    val windowDStream: DataStream[Double] = dataDstream
      .keyBy(0)
      .timeWindow(Time.seconds(15))
      .aggregate( MyAggregateFunc() )

    dataDstream.print("data")
    windowDStream.print("window")

    env.execute("window test job")
  }
}

/**
 * 自定義一個求溫度平均值的函數
 */
case class MyAggregateFunc() extends AggregateFunction[SensorReading, (Double, Int), Double]{

  // 狀態的初始值,溫度和計數都爲0
  override def createAccumulator(): (Double, Int) = (0.0, 0)

  // 函數所作操作 用之前的數據加上此時的數據,計數+1
  override def add(in: SensorReading, acc: (Double, Int)): (Double, Int) =
    (in.temperature + acc._1, acc._2 + 1)

  // 獲取數據的最終值
  override def getResult(acc: (Double, Int)): Double = acc._1 / acc._2

  // 可以直接寫null 這裏不會出現合併的現象,防止的話可以填一個聚合操作
  override def merge(acc: (Double, Int), acc1: (Double, Int)): (Double, Int) =
    (acc._1 + acc1._1, acc._2 + acc1._2)
}

在這裏插入圖片描述

其他可選API

在這裏插入圖片描述

Window API 總覽

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章