Flink:实时数据处理(4.Window API)

1.window 概念

将无限流切割为有限流的一种方式,它会将流数据分发到有限大小的桶(bucket)中进行分析

2.window 类型

2.1 时间窗口(Time Window)

2.1.1 滚动时间窗口

在这里插入图片描述

  • 将数据依据固定的窗口长度对数据进行切分
  • 时间对齐,窗口长度固定,没有重叠

2.1.2 滑动时间窗口

在这里插入图片描述

  • 滑动窗口由固定的窗口长度和滑动间隔组成
  • 窗口长度固定,可以有重叠

2.1.3 会话窗口

在这里插入图片描述

  • 由一系列事件组合一个指定时间长度的 timeout 间隙组成,也就是一段时间没有接收到新数据就会生成新的窗口
  • 特点:时间无对齐

2.2 计数窗口(Count Window)

2.2.1 滚动计数窗口

2.2.2 滑动计数窗口

3.window API

用 .window() 来定义一个窗口,然后基于这个 window 去做一些聚合或者其它处理操作。 window () 方法必须在 keyBy 之后才能用。 Flink 提供了更加简单的 .timeWindow 和 .countWindow 方法,用于定义时间窗口和计数窗口

3.1 窗口分配器(window assigner)

  • Window() 方法接收的输入参数是一个 WindowAssigner
  • WindowAssigner 负责将每条输入的数据分发到正确的 window 中
  • Flink 提供了通用的 WindowAssigner
    滚动窗口(tumbling window)
    滑动窗口(sliding window)
    会话窗口(session window)
    全局窗口(global window)

3.2 创建不同类型的窗口

在这里插入图片描述
在这里插入图片描述

3.3 窗口函数(window function)

window function 定义了要对窗口中收集的数据做的计算操作
可以分为两类

  1. 增量聚合函数(incremental aggregation functions)
    每条数据到来就进行计算,保持一个简单的状态
    ReduceFunction
    AggregateFunction

  2. 全窗口函数(full window functions)
    先把窗口所有数据收集起来,等到计算的时候会遍历所有数据
    ProcessWindowFunction:
    一些业务场景,我们需要收集窗口内所有的数据进行计算,例如计算窗口数据的中位数,或者计算窗口数据中出现频率最高的值。这样的需求,使用 ReduceFunction 和AggregateFunction就无法实现了。这个时候就需要 ProcessWindowFunction 了

3.3.1 案例:求窗口中最小温度值

import com.jaffe.day02.SensorSource
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time

/**
 * @Author jaffe
 * @Date 2020/06/10  14:10
 */
object MinTempPerWindow {
  def main(args: Array[String]): Unit = {

    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream = env.addSource(new SensorSource)

    stream
      .map(r => (r.id,r.temperature))
      .keyBy(_._1)
      .timeWindow(Time.seconds(10))
      .reduce((r1,r2) => (r1._1,r2._2.min(r1._2)))
      .print()
    env.execute()
  }
}

3.3.2 案例:使用增量聚合函数实现窗口温度平均值计算

import com.jaffe.day02.SensorSource
import org.apache.flink.api.common.functions.AggregateFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time

/**
 * @Author jaffe
 * @Date 2020/06/10  14:11
 */
object AvgTempPerWindow {
  def main(args: Array[String]): Unit = {

    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream = env
      .addSource(new SensorSource)

    stream
      .map(r => (r.id, r.temperature))
      .keyBy(_._1)
      .timeWindow(Time.seconds(5))
      .aggregate(new AvgTempFunction)
      .print()

    env.execute()
  }

  // 平均温度值 = 总的温度值 / 温度的条数
  class AvgTempFunction extends AggregateFunction[(String, Double),//输入的传感器ID和温度值
                                                  (String, Double, Long),//累加器的ID,目前温度总和,温度条数
                                                  (String, Double)//输出传感器ID和平均温度值
                                                   ] {
    // 创建累加器
    override def createAccumulator(): (String, Double, Long) = ("", 0.0, 0L)

    // 累加数据
    override def add(in: (String, Double), acc: (String, Double, Long)): (String, Double, Long) = {
      (in._1, acc._2 + in._2, acc._3 + 1)
    }
    //运算结果
    override def getResult(acc: (String, Double, Long)): (String, Double) = {
      (acc._1, acc._2 / acc._3)
    }
     //累加器间的合并
    override def merge(acc: (String, Double, Long), acc1: (String, Double, Long)): (String, Double, Long) = {
      (acc._1, acc._2 + acc1._2, acc._3 + acc1._3)
    }
  }
}

3.3.3 案例:全窗口聚合函数实现窗口平均值温度计算

下面例子:

  • process() 方法接受的参数为:window 的 key,Iterable 迭代器包含窗口的所有元素,Collector 用于输出结果流。Context 参数和别的 process 方法一样。
  • ProcessWindowFunction 的 Context 对象还可以访问 window 的元数据 (窗口开始和结束时间),当前处理时间和水位线,per-windowstate 和 per-key global state,side outputs。
  • per-window state: 用于保存一些信息,这些信息可以被 process() 访问,只要 process 所处理的元素属于这个窗口。
  • per-key global state: 同一个 key,也就是在一条 KeyedStream 上,不同的 window 可以访问 per-key global state 保存的值
import com.jaffe.day02.SensorSource
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.scala.function.ProcessWindowFunction
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
import org.apache.flink.util.Collector

/**
 * @Author jaffe
 * @Date 2020/06/10  14:50
 */
object AvgTempPerWindowByProcessWindowFunction {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream = env
      .addSource(new SensorSource)

    stream
      .map(r => (r.id, r.temperature))
      .keyBy(_._1)
      .timeWindow(Time.seconds(5))
      .process(new AvgTempFunc)
      .print()

    env.execute()

  }

  class AvgTempFunc extends ProcessWindowFunction[(String, Double), (String, Double), String, TimeWindow] {
    override def process(key: String, context: Context, elements: Iterable[(String, Double)], out: Collector[(String, Double)]): Unit = {
      val size = elements.size
      var sum = 0.0
      for (r <- elements) {
        sum += r._2
      }
      out.collect((key, sum / size))
    }
  }

3.3.4 案例:Aggregate(增量聚合和全窗口聚合结合使用计算窗口最大最小温度)

import com.jaffe.day02.{SensorReading, SensorSource}
import org.apache.flink.api.common.functions.AggregateFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.scala.function.ProcessWindowFunction
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
import org.apache.flink.util.Collector

/**
 * @Author jaffe
 * @Date 2020/06/10  15:36
 */
object MinMaxTempByAggregateAndProcess {
  case class MinMaxTemp(id: String,
                        min: Double,
                        max: Double,
                        endTs: Long)
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream = env.addSource(new SensorSource)

    stream
      .keyBy(_.id)
      .timeWindow(Time.seconds(5))
      // 第一个参数:增量聚合,第二个参数:全窗口聚合
      .aggregate(new Agg,new WindowResult)
      .print()

    env.execute()
  }
//增量聚合
  class Agg extends  AggregateFunction[SensorReading,(String,Double,Double),(String,Double,Double)]{
  override def createAccumulator(): (String, Double, Double) = {
    ("",Double.MaxValue,Double.MinValue)
  }

  override def add(in: SensorReading, acc: (String, Double, Double)): (String, Double, Double) = {
    (in.id,in.temperature.min(acc._2),in.temperature.max(acc._3))
  }

  override def getResult(acc: (String, Double, Double)): (String, Double, Double) = acc

  override def merge(acc1: (String, Double, Double), acc2: (String, Double, Double)): (String, Double, Double) = {
    (acc1._1,acc1._2.min(acc2._2),acc1._3.max(acc2._3))
  }
}

  class WindowResult extends ProcessWindowFunction[(String,Double,Double), MinMaxTemp,String,TimeWindow]{
    override def process(key: String, context: Context, elements: Iterable[(String, Double, Double)], out: Collector[MinMaxTemp]): Unit = {
      // 迭代器中只有一个值,就是增量聚合函数发送过来的聚合结果
      val minMax = elements.head
      out.collect(MinMaxTemp(key,minMax._2,minMax._3,context.window.getEnd))

    }
  }
}

3.3.5 案例:Reduce(增量聚合和全窗口聚合结合使用计算窗口最大最小温度)

import com.jaffe.day02.SensorSource
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.scala.function.ProcessWindowFunction
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
import org.apache.flink.util.Collector

/**
 * @Author jaffe
 * @Date 2020/06/10  16:26
 */
object MinMaxTempByReduceAndProcess {
  case class MinMaxTemp(id: String,
                        min: Double,
                        max: Double,
                        endTs: Long)
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream = env.addSource(new SensorSource)

    stream
      .map(r => (r.id, r.temperature, r.temperature))
      .keyBy(_._1)
      .timeWindow(Time.seconds(5))
      .reduce(
        (r1: (String, Double, Double), r2: (String, Double, Double)) => {
          (r1._1, r1._2.min(r2._2), r1._3.max(r2._3))
        },
        new WindowResult
      )
      .print()

    env.execute()
    
  }
  class WindowResult extends ProcessWindowFunction[(String, Double, Double),
    MinMaxTemp, String, TimeWindow] {
    override def process(key: String, context: Context, elements: Iterable[(String, Double, Double)], out: Collector[MinMaxTemp]): Unit = {
      val temp = elements.head
      out.collect(MinMaxTemp(temp._1, temp._2, temp._3, context.window.getEnd))
    }
  }
}

4.其它可选 API

.trigger() —— 触发器
定义 window 什么时候关闭,触发计算并输出结果

.evitor() —— 移除器
定义移除某些数据的逻辑

.allowedLateness() —— 允许处理迟到的数据

.sideOutputLateData() —— 将迟到的数据放入侧输出流

.getSideOutput() —— 获取侧输出流

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值