一、前言
本文主要关于Flink1.12.0中关于时间语义,watermark,以及windows的一些使用和理解。
二、背景
准备重新梳理下flink的相关应用,以及flink源码解读,更深入的了解和学习下flink的使用。开始部分文章会写flink应用层的使用,以及一些代码例子。后面会针对源码进行剖析~~
三、时间语义、watermark
在flink中时间语义常用有以下两种
1、eventtime 事件时间,可以理解这条记录产生的时间 ,一般都是在记录的某个字段里。
2、processtime 处理时间,这是flinktask处理时的系统时间,可以理解为数据到task后开始处理的时间 。
定义时间语义有啥用呢?
我理解是为了窗口计算。因为窗口需要通过时间来划分,这个时间就需要你上面自己定义。
那么watermark又是干啥的?
watermark是为了允许乱序和迟来的数据。举个例子,现在有个场景需要每十分钟统计过去十分钟的订单数据,假设这个订单数据在kakfa中时乱序的,某个分区的订单时间顺序可能是 10:01 ,10:03,10:08,10:11,10:09,10:13
在上面的顺序中如果按照正常时间执行的话窗口是10:00-10:10这个区间,所以在10:11这条数据来了后,这个窗口就相当于被触发了,那么后面10:09这条数据就不会再被计算到。。这个时候如果想让这条记录被处理到,可以加入水印-2min,当10:11这条记录来的时候他的watermark是10:11-2min=10:09 那么这个窗口还是未被触发的,后面的10:09就不会被遗漏。当10:13这条记录来的时候才会被触发~
下面来看看watermark和eventtime生成的代码:
测试类 关于eventtime的例子:接入订单流数据,设置eventtime字段,并设置水印延迟2s,然后按5s的窗口滚动计算窗口内的订单金额。~
package com.realtime.flink.test
import java.time.Duration
import com.realtime.flink.dto
import com.realtime.flink.dto.OrderDto
import com.realtime.flink.source.OrderSource
import org.apache.flink.api.common.eventtime.{
SerializableTimestampAssigner, WatermarkStrategy}
import org.apache.flink.api.common.functions.{
AggregateFunction, RichFlatMapFunction}
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.assigners.{
TumblingEventTimeWindows, TumblingProcessingTimeWindows}
import org.apache.flink.streaming.api.windowing.time.Time
object EventTimeTest {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
val conf = env.getConfig
conf.setAutoWatermarkInterval(1000)
val strategy = WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(2)).withTimestampAssigner(new SerializableTimestampAssigner[OrderDto] {
override def extractTimestamp(element: OrderDto, recordTimestamp: Long): Long = {
element.getOrderTime
}
});
env.addSource(new OrderSource)
.assignTimestampsAndWatermarks(strategy)
.windowAll(TumblingEventTimeWindows.of(Time.seconds(5)))
.aggregate (new AggregateFunction[OrderDto,(String,Double),(String,Double)] {
override def createAccumulator(): (String, Double) = {
("",0L)
}
override def add(in: OrderDto, acc: (String, Double)): (String, Double) = {
("",in.getOrderPrice+acc._2)
}
override def getResult(acc: (String, Double)): (String, Double) = {
acc
}
override def merge(acc: (String, D