一、时间语义概念
- Event Time : 事件时间,记录的是设备生产(或者存储)事件时间
- Ingestion Time : 摄取时间,Flink读取事件的时间
- Processing Time : 处理时间,执行操作算子的本地系统时间,与机器有关
在生产环境中我们往往更关心事件时间
二、水位线(Watermark)
让我们通过一个简单的示例来演示为什么需要 watermarks 及其工作方式。
在此示例中,我们将看到带有混乱时间戳的事件流,如下所示。显示的数字表达的是这些事件实际发生时间的时间戳。到达的第一个事件发生在时间 4,随后发生的事件发生在更早的时间 2,依此类推:
··· 23 19 22 24 21 14 17 13 12 15 9 11 7 2 4 →
正是因为生产中常常会有以上情况发生,所以引入了水位线,主要是为了避免乱序数据带来计算不正确,相当于为了结果正确性而设置了延迟等待
三、如何设置事件时间和水位线
通常我们在使用窗口函数的时候需要设置时间语义,当我们没有手动设置时间语义时,默认是使用processing time,也就是系统时间,所以接下来是以设置事件时间来说明
import org.apache.flink.streaming.api.scala._
import org.apache.flink.api.common.eventtime.{SerializableTimestampAssigner, WatermarkStrategy}
case class Sensor1(id: String, temp: Double, time: Long)
object StreamWindow {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
val strategy = WatermarkStrategy.forBoundedOutOfOrderness[Sensor1](Duration.ofSeconds(1)) //延迟1秒
.withTimestampAssigner(new SerializableTimestampAssigner[Sensor1] {
override def extractTimestamp(element: Sensor1, recordTimestamp: Long): Long = element.time * 1000L //指定事件时间字段
})
val dataStream = env.socketTextStream("localhost", 9999)
.map(data => {
val arr = data.split(",")
Sensor1(arr(0), arr(1).toDouble, arr(2).toLong)
})
.assignTimestampsAndWatermarks(strategy) //设置时间及水位线
val resultStream = dataStream.map(data => (data.id, data.temp, data.time))
.keyBy(_._1)
.window(TumblingEventTimeWindows.of(Time.seconds(10))) //滚动窗口
.reduce((curdata, newdata) => (curdata._1, curdata._2.min(newdata._2), newdata._3))
resultStream.print()
env.execute("stream window")
}
}
四、总结
以上就是今天要讲的内容,本文仅仅简单介绍了事件时间和水位线及其使用,而Flink有大量的函数和方法供我们使用