![](https://img-blog.csdnimg.cn/2021080516583229.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80ODY3ODkyMw==,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20210805165839594.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80ODY3ODkyMw==,size_16,color_FFFFFF,t_70)
将已经到的所有数据时间戳最大的时间再减去一个固定的延迟时间,做为关闭窗口的依据,如:某时刻已经来的数据时间最大的为5,延迟时间为2,那么该关闭的窗口就为[0,3),这里时间是从零时刻开始的。
1.无乱序数据,防止数据漏掉的方法
.assignAscendingTimestamps(_.timestamp * 1000L) //
升序数据提取时间戳
,
该方法用于没有乱序数据的情况下
2.有乱序情况下:
a.每来一个数据生产一个设置最大延迟时间
b.根据最大乱序来周期性生产设置最大延迟时间
1).尽可能将最大乱序程度作为assignTimestampsAndWatermarks的值,这样可以保证数据不丢失,如果设置过大会影响实时性
.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[ApacheLogEvent](Time.seconds(1)) { //最大乱序程度,更加要处理的数据来查看
override def extractTimestamp(element: ApacheLogEvent): Long = element.timestamp
}
2).通过设置窗口关闭时间来保证数据不丢失,在开窗之前设置allowedLateness
方法为:
//延迟窗口关闭时间来处理乱序数据,窗口登一分钟再关闭
.allowedLateness(Time.minutes(1))
3).如果了为实时性和效率考虑,上述两种方法很难保证数据不丢失,就可以将迟到的数据放在测输出流中,方法为
.sideOutputLateData(new OutputTag[ApacheLogEvent]("late"))
数据将会存放在late测输出流中,获取方法为:
val latevalue = aggStream.getSideOutput(new OutputTag[ApacheLogEvent]("late"))
案例:
样例类:
case class SensoreReding(id: String , timestamp: Long , temperature: Double)
核心代码:
package com.guqunyong.window
import com.guqunyong.APITest.SensoreReding
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time
/**
* 处理数据乱序问题
* @author shkstart
* @create 2021-07-24 11:15
*/
object WindowTest {
def main(args: Array[String]): Unit = {
//创建环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.getConfig.setAutoWatermarkInterval(50)
//读取数据
val value = env.socketTextStream("localhost", 7777)
val dataStream = value.map(
data => {
val fields = data.split(",")
SensoreReding(fields(0), fields((1)).toLong, fields(2).toDouble)
}
)
// .assignAscendingTimestamps(_.timestamp * 1000L) //升序数据提取时间戳,该方法用于没有乱序数据的情况下
.assignTimestampsAndWatermarks(
new BoundedOutOfOrdernessTimestampExtractor[SensoreReding](Time.seconds(3)) {//最大乱序,即为最大延迟时间设置为3s
override def extractTimestamp(element: SensoreReding): Long = element.timestamp * 1000L
}
)
//每15秒统计一次,窗口内各传感器所有温度的最小值
val resultStream = dataStream
.map(data => (data.id,data.temperature,data.timestamp))
.keyBy(_._1)
.timeWindow(Time.seconds(15))
.allowedLateness(Time.minutes(1)) //处理迟到1分钟的数据
.sideOutputLateData(
//将将漏掉的数据放到测输出流中处理
new OutputTag[(String, Double, Long)]("late")
)
//.minBy(1)
.reduce(
(curRes,newData) => (curRes._1,curRes._2.min(newData._2),newData._3)
)
resultStream.print()
//执行
env.execute("window test")
}
}