Flink时间语义与watermark的原理

时间语义

  • 我们可以直接在代码中,对执行环境调用setStreamTimeCharacteristic方法,设置流的时间特性
	val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //EventTime 事件发生事件
    //IngestionTime 事件进入Flink事件
    //ProcessingTime 事件处理事件
    //给env创建的每一个Stream追加时间特性
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
  • 具体的时间,还需要从数据中提取时间戳(timestamp)

在 Flink的流式处理中, 绝大部分的业务都会使用 eventTime一般只在eventTime 无法使用时,才会被迫使用 ProcessingTime 或者 IngestionTime

乱序数据的影响

在这里插入图片描述

  • 当Flink 以Event Time模式处理数据流时,它会根据数据里的时间戳来处理基于时间的算子
  • 由于网络、分布式等原因,会导致乱序数据的产生
  • 这就衍生出来一个问题,如上图所示,假设窗口为0-5,当接收到数据5时,就关闭的了窗口,那迟到的数据2、3,就会丢失,那怎么处理这种情况呢?所以就有了watermark机制

Watermark(水位线)

  • 怎样避免乱序数据带来计算不正确?
  • 遇到一个时间戳达到了窗口关闭时间,不应该立刻触发窗口计算,而是等待一段时间,等迟到的数据来了再关闭窗口
  • Watermark是一种衡量Event Time进展的机制,可以设定延迟触发
  • Watermark是用于处理乱序事件的,而正确的处理乱序事件,通常用Watermark机制结合window来实现;
  • 数据流中的Watermark用于表示timestamp小于Watermark的数据,都已经到达了,因此,window的执行也是由Watermark触发的。
  • watermark用来让程序自己平衡延迟和结果正确性

Watermark特点

在这里插入图片描述

  • watermark 是一条特殊的数据记录
  • watermark必须单调递增,以确保任务的事件时间时钟在向前推进,而不是在后退.
  • watermark与数据的时间戳相关

watermark的传递

在这里插入图片描述

  • 上游通过广播的方式将watermark传递至下游,下游会有一个watermark的分区来接收多个并行子任务广播的watermark值

Watermark的使用

import Source.WaterSensor
import org.apache.flink.api.common.state._
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

//定义带时间戳字段的样例类
case class WaterSensor(id:String,ts:Long,vc:Double)
object FlinkEventTime_wt {
    
    
  def main(args: Array[String]): Unit = {
    
    
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //为env创建的每一个Stream追加时间特性
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

	//从端口读取数据
    val dataStream: DataStream[String] = env.socketTextStream("192.168.**.**",7777)
	
	//将读取的数据转换成WaterSensor类型
    dataStream.map(x=>{
    
    
      val strings: Array[String] = x.split(",")
      WaterSensor(strings(0),strings(1).toLong,strings(2).toDouble)
    })
      //方式一(推荐):数据密集时,可以使用周期性生成watermark
      .assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[WaterSensor](Time.seconds(3)) {
    
    //设置乱序延迟为3秒
      		//将处理过的数据提取出时间戳,乘以1000转成以毫秒的形式计数
      		override def extractTimestamp(t: WaterSensor): Long = t.ts*1000L
      })
      //方式二:分配升序时间戳,传入毫秒时间戳
      //.assignAscendingTimestamps(_.ts*1000L)
    
	//设置窗口为10秒并以及3秒的长度滑动
    val dataStream3: WindowedStream[(String, Double), String, TimeWindow] = dataStream2.map(x=>(x.id,x.vc)).keyBy(x=>x._1).timeWindow(Time.seconds(10),Time.seconds(3))

    //对window中的数据进行的处理
    val dataStream4: DataStream[(String, Double)] = dataStream3.reduce((x,y)=>(x._1,x._2+y._2))
    
    //输出window中的数据
    dataStream4.print("ev_wate_win")
    
    //启动
    env.execute("watermark")
  }
}

猜你喜欢

转载自blog.csdn.net/weixin_38468167/article/details/112059125