事件时间(event time)与水印(watermark)
事件时间和水印诞生的背景
在实际的流式计算中数据到来的顺序对计算结果的正确性有至关重要的影响
比如:某数据源中的某些数据由于某种原因(如:网络原因,外部存储自身原因)会有2秒的延时,也就是在实际时间的第1秒产生的数据有可能在第3秒中产生的数据之后到来。
假设在一个5秒的滚动窗口中,有一个EventTime是 9秒的数据,在第11秒时候到来了。
图示:
- 那么对于一个Count聚合的Tumble(5s)的window,上面的情况如何处理才能window3=3,window2=3 呢?
时间类型
Flink支持不同的时间概念
Processing Time(处理时间)
- 处理时间是指当前机器处理该条事件的时间。
- 它是当数据流入到具体某个算子时候相应的系统。
- 他提供了最小的延时和最佳的性能。
- 但是在分布式和异步环境中, 处理时间不能提供确定性。
- 因为其对时间到达 系统的速度和数据流在系统的各个operator 之间处理的速度很铭感。
Event Time(事件时间)
- 事件时间是每个事件在其生产设备上发生的时间。
- 此时间通常在进入Flink之前嵌入到记录中,并且可以从每个记录中提取该事件时间戳。
- 事件时间对于乱序、延时、或者数据重放等情况,都能给出正确的结果。
- 事件时间依赖于事件本身,而跟物理时钟没有关系。
- 基于事件时间的程序必须指定如何生成事件时间水印(watermark),这是指示事件时间进度的机制。
- 事件时间处理通常存在一定的延时,因此需要为延时和无序的事件等待一段时间。
- 因此,使用事件时间编程通常需要与处理时间相结合。
Ingestion Time(摄入时间)
- 摄入时间是数据进入Flink框架的时间,是在Source Operator中设置的
- 与ProcessingTime相比可以提供更可预测的结果,因为摄入时间的时间戳比较稳定(在源处只记录一次)
- 同一数据在流经不同窗口操作时将使用相同的时间戳
- 而对于ProcessingTime同一数据在流经不同窗口算子会有不同的处理时间戳
Process time 与 Event time对比:
- 如上图所示,在一个乱序的数据流里,使用event time类型的事件时间,可以保证数据流的顺序性。
设置时间特行
Flink程序的第一部分工作通常是设置时间特性,该设置用于定义数据源使用什么时间,在时间窗口处理中使用什么时间。
代码:
// 设置执行环境, 类似spark中初始化SparkContext
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime); // env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime); // env.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime);
Watermark (水印)
WaterMark 产生背景
- 流处理从事件产生,到数据流经source,再到operator,中间是有一个过程和时间的。
- 虽然大部分情况下,数据流到operator的数据都是按照事件产生的时间顺序来的,但是也不排除由于网络、背压等原因,导致乱序的产生(out-of-order或者说late element)。
- 但是对于late element(延迟数据),我们又不能无限期的等下去,必须要有个机制来保证一个特定的时间后,必须触发window去进行计算了。
- 这个特别的机制,就是watermark。
WaterMark 介绍
- Watermark是Flink为了处理EventTime时间类型的窗口计算提出的一种机制, 本质上也是一种时间戳。
- Watermark是用于处理乱序事件的,而正确的处理乱序事件,通常用watermark机制结合window来实现。
- 当operator通过基于Event Time的时间窗口来处理数据时,它必须在确定所有属于该时间窗口的消息全部流入此操作符后,才能开始处理数据。
- 但是由于消息可能是乱序的,所以operator无法直接确认何时所有属于该时间窗口的消息全部流入此操作符。
- WaterMark包含一个时间戳,Flink使用WaterMark标记所有小于该时间戳的消息都已流入
- Flink的数据源在确认所有小于某个时间戳的消息都已输出到Flink流处理系统后,会生成一个包含该时间戳的WaterMark,插入到消息流中输出到Flink流处理系统中,Flink operator算子按照时间窗口缓存所有流入的消息。
- 当操作符处理到WaterMark时,它对所有小于该WaterMark时间戳的时间窗口的数据进行处理并发送到下一个操作符节点,然后也将WaterMark发送到下一个操作符节点。
WaterMark 的产生方式
- Punctuated
- 数据流中每一个递增的EventTime都会产生一个Watermark。
- 在实际的生产中Punctuated方式在TPS很高的场景下会产生大量的Watermark在一定程度上对下游算子造成压力,所以只有在实时性要求非常高的场景才会选择Punctuated的方式进行Watermark的生成。
- Periodic
- 周期性的(一定时间间隔或者达到一定的记录条数)产生一个Watermark。
- 在实际的生产中Periodic的方式必须结合时间和积累条数两个维度继续周期性产生Watermark,否则在极端情况下会有很大的延时。
- Punctuated
代码:
package com.ronnie.flink.stream.test; import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.java.tuple.Tuple;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.AssignerWithPeriodicWatermarks;
import org.apache.flink.streaming.api.functions.windowing.WindowFunction;
import org.apache.flink.streaming.api.watermark.Watermark;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector; import javax.annotation.Nullable;
import java.text.ParseException;
import java.text.SimpleDateFormat; /**
*
hello,2019-09-17 11:34:05.890
hello,2019-09-17 11:34:07.890
hello,2019-09-17 11:34:13.890
hello,2019-09-17 11:34:08.890
hello,2019-09-17 11:34:16.890
hello,2019-09-17 11:34:19.890
hello,2019-09-17 11:34:21.890
*/
public class WaterMarkTest {
public static void main(String[] args) {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime); env.setParallelism(1); // 设置多久查看一下当前的水位线... 默认200ms
env.getConfig().setAutoWatermarkInterval(10000); System.err.println("interval : " + env.getConfig().getAutoWatermarkInterval()); DataStreamSource<String> streamSource = env.socketTextStream("ronnie01", 9999); SingleOutputStreamOperator<String> watermarks = streamSource.assignTimestampsAndWatermarks(new MyWaterMark()); watermarks.map(new MapFunction<String, Tuple2<String, Integer>>() {
@Override
public Tuple2<String, Integer> map(String value) throws Exception {
String[] split = value.split(",");
String key = split[0]; return new Tuple2<String, Integer>(key, 1);
}
}).keyBy(0)
.timeWindow(Time.seconds(10))
// 自定义的一个计算规则......
.apply(new MyWindowFunction())
.printToErr(); try {
env.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
} class MyWaterMark implements AssignerWithPeriodicWatermarks<String>{ // 目前系统里所有数据的最大事件时间
long currentMaxTimeStamp = 0;
// 允许数据延迟5s
long maxLateTime = 5000; Watermark wm = null; SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @Nullable
@Override
// 周期性地获取目前的水位线时间, 默认200ms
public Watermark getCurrentWatermark() {
// 未处理的延迟/乱序问题
// wm = new Watermark(currentMaxTimeStamp); // 处理数据的延迟/乱序问题
wm = new Watermark(currentMaxTimeStamp - maxLateTime);
System.out.println(format.format(System.currentTimeMillis()) + " 获取当前水位线: " + wm + ","+ format.format(wm.getTimestamp()));
return wm;
} @Override
public long extractTimestamp(String element, long previousElementTimestamp) {
String[] split = element.split(","); String key = split[0]; long timestamp = 0; try {
//将2019-09-17 10:24:50.958 格式时间转成时间戳
timestamp = format.parse(split[1]).getTime();
} catch (ParseException e) {
e.printStackTrace();
} // 对比新数据的时间戳和目前最大的时间戳, 取大的值作为新的时间戳
currentMaxTimeStamp= Math.max(timestamp, currentMaxTimeStamp); System.err.println(key +", 本条数据的时间戳: "+ timestamp + "," +format.format(timestamp)
+ "|目前数据中的最大时间戳: "+ currentMaxTimeStamp + ","+ format.format(currentMaxTimeStamp)
+ "|水位线时间戳: "+ wm + ","+ format.format(wm.getTimestamp())); return timestamp;
}
} class MyWindowFunction implements WindowFunction<Tuple2<String, Integer>, String, Tuple, TimeWindow>{ @Override
public void apply(Tuple tuple, TimeWindow window, Iterable<Tuple2<String, Integer>> input, Collector<String> out) throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); int sum = 0; for (Tuple2<String, Integer> tuple2:input){
sum += tuple2.f1;
}
long start = window.getStart();
long end = window.getEnd(); out.collect("key:" + tuple.getField(0) + " value: " + sum + "| window_start :"
+ format.format(start) + " window_end :" + format.format(end)
);
}
}
事件时间(event time)与水印(watermark)的更多相关文章
- 【源码解析】Flink 是如何基于事件时间生成Timestamp和Watermark
生成Timestamp和Watermark 的三个重载方法介绍可参见上一篇博客: Flink assignAscendingTimestamps 生成水印的三个重载方法 之前想研究下Flink是怎么处 ...
- Flink架构(三)- 事件-时间(Event-Time)处理
3. 事件-时间(Event-Time)处理 在“时间语义”中,我们强调了在流处理应用中时间语义的重要性,并解释了处理时间与事件时间的不同点.处理时间较好理解,因为它基于本地机器的时间,它产生的是有点 ...
- 「Flink」事件时间与水印
我们先来以滚动时间窗口为例,来看一下窗口的几个时间参数与Flink流处理系统时间特性的关系. 获取窗口开始时间Flink源代码 获取窗口的开始时间为以下代码: org.apache.flink.str ...
- Flink Streaming基于滚动窗口的事件时间分析
使用flink-1.9.0进行的测试,在不同的并行度下,Flink对事件时间的处理逻辑不同.包括1.1在并行度为1的本地模式分析和1.2在多并行度的本地模式分析两部分.通过理论结合源码进行验证,得到具 ...
- [DOM Event Learning] Section 2 概念梳理 什么是事件 DOM Event
[DOM Event Learning] Section 2 概念梳理 什么是事件 DOM Event 事件 事件(Event)是用来通知代码,一些有趣的事情发生了. 每一个Event都会被一个E ...
- 事件(event),正则
1.事件(event):事件是可以被 JavaScript 侦测到的行为.网页中的每个元素都可以产生某些可以触发 JavaScript 函数的事件.2.事件源: 触发事件的元素 事件: 被 JavaS ...
- TI-RTOS 之 事件同步(Event, 类似semaphore)
TI-RTOS 之 事件同步(Event, 类似semaphore) Event 是类似Semaphore的存在,官方如下描述: SYS/BIOS events are a means of comm ...
- C++多线程同步之事件(Event)
原文链接:http://blog.csdn.net/olansefengye1/article/details/53291074 一.事件(Event)原理解析 1.线程同步Event,主要用于线程间 ...
- 锁机制(Lock) 信号量机制(Semaphore) 事件机制(Event)
IPC 进程间通信(inter-Process Communicate) 锁机制(Lock) l = Lock() 开启一个锁机制(实例化) 一把锁配一个钥匙 l.acquire() 获得钥匙 ...
随机推荐
- 使用MyCat实现MySQL读写分离
说明 配置MyCat读写分类前需要先配置MySQL的主从复制,参考我上一篇的文章,已经做了比较详细地讲解了. 环境 centos7.MySQL5.7.mycat1.6 配置MyCat账号密码和数据库名 ...
- 子组件props接受父组件传递的值 能修改吗?
vue2.0 子组件props接受父组件传递的值,能不能修改的问题整理 父组件代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...
- MFC CListCtrl 显示bmp图片
m_ListCtrl.SetExtendedStyle(m_ListCtrl.GetExtendedStyle()| LVS_EX_SUBITEMIMAGES | LVS_EX_GRIDLINES); ...
- 100、Java中String类之字符串转为大写
01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...
- java实现在线预览 - -之poi实现word、excel、ppt转html
简介 java实现在线预览功能是一个大家在工作中也许会遇到的需求,如果公司有钱,直接使用付费的第三方软件或者云在线预览服务就可以了,例如永中office.office web 365(http://w ...
- Slim安装以及使用
最近在用backbone.js 做东西,因为牵扯到REST services 所以需要后台支持,此处选择了php.Slim 是php的一个框架. 貌似国内文章对此的介绍比较少,在安装Slim的过程中出 ...
- 【Unity】鼠标划定范围然后截图~
有时候要重复用某一个场景的某一个角度,都过去好几步了结果总不能再把已经打乱的场景物体再移动回去吧.so~智慧的我完成了伟大的偷懒.截图保存,什么时候要看,直接上图片以假乱真棒棒哒~ 当然这个功能还能用 ...
- VS中MFC项目文件特别大的解决办法
转 来自http://m.zhizuobiao.com/vc/vc-18082800177/ 自己插个眼 项目文件比较大因为 项目下有个隐藏文件夹.vs 下面是解决办法 本文主要向大家介绍了VC编程 ...
- JForum项目搭建
JForum 是采用Java开发的功能强大且稳定的论坛系统.它提供了抽象的接口.高效的论坛引擎以及易于使用的管理界面,同时具有完全的权限控制.多语言支持(包括中文).高性能.可自定义的用户接口.安全. ...
- Java并发编程之并发简介
操作系统中同时执行多个程序原因: 1.资源利用率:系统资源及硬件资源.当一个程序再等待另一个程序时,可以运行另一个程序,可提高资源利用率. 2.公平性:多个程序对计算机上的资源理论上具有同等的使用权. ...