时间语义
- 我们可以直接在代码中,对执行环境调用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")
}
}