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 }
本文来自博客园,作者:小白啊小白,Fighting,转载请注明原文链接:https://www.cnblogs.com/ywjfx/p/14254311.html