window+trigger+watermark处理全局乱序数据,指定窗口上的allowedLateness可以处理特定窗口操作的局部事件时间乱序数据

1、流处理系统中的微批

Flink内部也使用了某种形式的微批处理技术,在shuffle阶段将含有多个事件的缓冲容器通过网络发送,而不是发送单个事件
流处理系统中的批处理必须满足以下两点要求:

  • 批处理只作为提高系统性能的机制。批量越大,系统的吞吐量就越大。
  • 为了提高性能而使用的批处理必须完全独立于定义窗口时所用的缓冲,或者为了保证容错性而提交的代码,也不能作为 API 的一部分。否则,系统将受到限制,并且变得脆弱且难以使用。

2、时间概念

  • 事件时间,即事件实际发生的时间(由水印触发器实现),基于事件时间处理可实现时间回溯并正确地重新处理数据
  • 处理时间,即事件被处理的时间,是处理事件的机器所测量的时间
  • 摄取时间,即事件进入流处理框架的时间,缺乏事件时间的数据会被处理器附上摄取时间(由source函数完成)

3、窗口

所有内置窗口都由同一种机制实现,开窗机制与检查点机制完全分离;可直接用基本的开窗机制定义更复杂的窗口(如某种时间窗口,可基于元素计数生成中间结果)
窗口时间区间是按自然时间分配的,比如3秒的时间间隔,[0,3) [3,6)
(1)时间窗口(每隔B时长对A时长内数据聚合)

  • 设置事件时间 env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
  • 设置处理时间 env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime)
  • 设置摄取时间 env.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime)
  • 滚动窗口A stream.timeWindow(Time.minutes(1)) stream.window(TumblingEventTimeWindows.of(Time.seconds(1)))
  • 滑动窗口B stream.timeWindow(Time.minutes(1), Time.seconds(30)) stream.window(TumblingEventTimeWindows.of(Time.seconds(1)), SlidingEventTimeWindows.of(Time.seconds(30)))

(2)计数窗口(每隔B个元素对A个元素进行聚合)
为避免永远达不到计数窗口而浪费内存,可用时间窗口触发超时

  • 滚动窗口A stream.countWindow(4)
  • 滑动窗口B stream.countWindow(4, 2)

(3)会话窗口(会话即活动阶段,其前后都是非活跃阶段,常用于无固定持续时间或无固定交互次数的场景)
由超时时间设定,即希望非活跃状态持续多久才结束窗口。window区间:当b比上一条记录a延迟超过超时时间t时,出现会话窗口[上一个window_end, b-t)

  • 事件时间会话窗口 stream.window(EventTimeSessionWindows.withGap(Time.minutes(5))
  • 处理时间会话窗口 stream.window(ProcessingTimeSessionWindows.withGap(Time.minutes(5))

处理延迟数据

  • allowedLateness(Time.minutes(60))

缩短反馈时间(若用户会话迟迟不结束,反馈时间过长)

  • trigger(ContinuousEventTrigger.of(Time.minutes(10)) #每10分钟输出一个结果并覆盖之前的

(4)全局窗口(对全部数据进行统计,使用流方法实现批处理)
内置触发器是NeverTrigger,永远不会触发,需要自定义触发器才有意义
stream.window(GlobalWindows.create()).trigger(...)

4、触发器

继承Trigger类,Trigger抽象类的结构:

boolean canMerge()
void clear(W window, TriggerContext ctx)
TriggerResult onElement(T element, long timestamp, W window, TriggerContext ctx) 每个元素到来时执行
TriggerResult onEventTime(long time, W window, TriggerContext ctx) Timer到期后执行
void onMerge(W window, OnMergeContext ctx)
TriggerResult onProcessingTime(long time, W window, TriggerContext ctx)

5、水印

窗口 + 水印,用于解决乱序问题(并不是解决,而是假定所有正常的事件都只是一定程度内乱序,可以解决此程度内的乱序)

当Watermark在红色区域时,窗口内的元素会计算

(1)基于事件时间处理时,水印是判断所有事件到达的标志,开始计算和输出结果,晚于处理时间但早于此水印时间的事件也可被正确处理。
水印定义最长迟到数据(比当前watermark还早的数据会被丢弃,水印阈值越大,允许的迟到数据越久)
watermark的值不是全局的,但与key无关,有几个并行,就有几个watermark,window的触发条件与最小的watermark有关
水印时间 = 收到的最大事件时间 - 水印阈值

一个操作算子收到多个并行流的输入时,取最小的watermark作为当前算子的watermark

(2)异常情况:如果水印迟到得太久(可能是maxOrderness设置太大,也可能是后序事件过晚到达),收到结果的速度会变慢,解决方法是在水印到达之前输出近似结果,其实就是后面设置Lateness的方案;如果水印到达得太早(可能是maxOrderness设置太小,也可能是后序事件过早到达),则可能丢失一些前序事件,收到错误结果,解决方法是采用Flink作业监控事件流,学习事件的迟到规律,以此构建水印模型
(3)分配Timestamp和Watermark
timestamp和watermark都是通过从1970年1月1日0时0分0秒到现在的毫秒数来指定的
先后顺序:分配timstamp是按设置的时间间隔定时执行的,即使无数据进来也会执行,这就造成了getCurrentWatermark调用后看上去第一个watermark永远是以0为基准计算显示的 ,但实际并不是按那个算的。第2条的watermark如果是23的话,是不大于window_end 24的,也就不应该触发,而如果是下一条的24则可以触发。AssignerWithPeriodicWatermarks子类是每隔一段时间执行的,这个具体由ExecutionConfig.setAutoWatermarkInterval设置,如果没有设置会几乎没有间隔地调用getCurrentWatermark方法。之所以会出现-10000时因为你没有数据进入窗口,当然一直都是-10000,但是getCurrentWatermark方法不是在执行extractTimestamp后才执行的

直接在数据源生成(推荐,数据生成时即分配timestamp和watermark)
实现SourceFuntion接口的run方法,并调用如下方法:

  • 分配timestamp:SourceContext.collectWithTimestamp(...)
  • 分配watermark:SourceContext.emitWatermark(new WaterMark(...))

获取流后使用生成器生成新流(使用此种方式,会覆盖源提供的timestamp和watermark,注意一定要在时间窗口之前生成)
stram.assignTimestampsAndWatermarks( AssignerWithPeriodicWatermarks/AssignerWithPunctuatedWatermarks 实现类对象)
定义分配器
AssignerWithPeriodicWatermarks(周期性水印,分配时间戳并定期生成水印)
watermark产生的事件间隔(每n毫秒)是通过ExecutionConfig.setAutoWatermarkInterval(...)来定义的,当getCurrentWatermark()被调用时,若返回的watermark非空且大于上一个watermark,则发射一个新的watermark

  • 预定义实现类(使用时重写extractTimestamp):
    • AscendingTimestampExtractor 适用于时间戳递增的情况
    • BoundedOutOfOrdernessTimestampExtractor 适用于乱序但最大延迟已知的情况
  • 自定义实现类(使用时重写getCurrentWatermark、extractTimestamp)

AssignerWithPunctuatedWatermarks(带断点水印)
事件驱动生成水印,每个单独的event都可以产生一个watermark,会有额外计算,过多可能导致性能降低。任何一个event都触发extractTimestamp(...)来为元素分配一个timestamp,然后立即调用该元素上的checkAndGetNextWatermark(...)方法,一旦checkAndGetNextWatermark(...)返回一个非空的watermark并且watermark比前一个watermark大的话,这个新的watermark将会被发送
(4)设定水印后触发window的条件:

  • watermark >= window_end(开启多并发后,每个算子接收到的watermark都会进行对齐,取最小的watermark作为最终的watermark并往下一个算子发送)
  • 在[window_begin, window_end)中有数据存在

(5)不足之处
无法应对迟到数据,如果一个窗口已经被触发了,即使满足上述条件也不会第二次触发窗口。水印被发射到下一个算子前已默认比水印更早的数据已经全部处理了

6、allowedLateness

主要用于解决迟到问题,给迟到数据第二次或多次触发window的机会,可对无法触发window的迟到数据单独处理
默认情况下,watermark超过end-of-window后,将忽略之后到达的符合window的数据
在Watermark < 窗口结束时间 + Lateness时,仍会继续等待窗口内的元素参与窗口计算,计算时要注意状态值的重复,直到Watermark >= 窗口结束时间 + Lateness 时清空缓存
要注意再次触发窗口时,UDF中的状态值的处理,要考虑state在计算时的去重问题
(1)

  • 对于trigger是默认的EventTimeTrigger的情况,allowedLateness会再次触发窗口的计算,而之前触发的数据,会buffer起来,直到watermark超过end-of-window + allowedLateness的时间,窗口的数据及元数据信息才会被删除。再次计算就是DataFlow模型中的Accumulating的情况。
  • 对于sessionWindow情况,当late element在allowedLateness范围之内到达时,可能会引起窗口的merge,这样,之前窗口的数据会在新窗口中累加计算,这就是DataFlow模型中的AccumulatingAndRetracting的情况。

(2)触发条件

  • watermark < window_end + allowedLateness
  • 在[window_begin, window_end)中有late数据存在

7、定时器Timer

Flink Streaming API提供的用于感知并利用处理时间/事件时间变化的机制
(1)在KeyedProcessFunction实现类里定义定时器为例:
重写processElement(),对每个输入元素注册定时器
重写onTimer(),定时器触发时执行的逻辑
根据时间特征的不同,具体如下:
处理时间——调用Context.timerService().registerProcessingTimeTimer()注册;onTimer()在系统时间戳达到Timer设定的时间戳时触发。
事件时间——调用Context.timerService().registerEventTimeTimer()注册;onTimer()在Flink内部水印达到或超过Timer设定的时间戳时触发。
(2)EventTimeTrigger使用Timer实现触发时间窗口

@Override
public TriggerResult onElement(Object element, long timestamp, TimeWindow window, TriggerContext ctx) throws Exception {
if (window.maxTimestamp() <= ctx.getCurrentWatermark()) { return TriggerResult.FIRE; }
else { ctx.registerEventTimeTimer(window.maxTimestamp()); return TriggerResult.CONTINUE; }
}

  

Flink之对时间的处理的更多相关文章

  1. Flink学习(二)Flink中的时间

    摘自Apache Flink官网 最早的streaming 架构是storm的lambda架构 分为三个layer batch layer serving layer speed layer 一.在s ...

  2. 「Flink」Flink中的时间类型

    Flink中的时间类型和窗口是非常重要概念,是学习Flink必须要掌握的两个知识点. Flink中的时间类型 时间类型介绍 Flink流式处理中支持不同类型的时间.分为以下几种: 处理时间 Flink ...

  3. Flink基础:时间和水印

    ​ 往期推荐: Flink基础:入门介绍 Flink基础:DataStream API Flink基础:实时处理管道与ETL Flink深入浅出:资源管理 Flink深入浅出:部署模式 Flink深入 ...

  4. 第08讲:Flink 窗口、时间和水印

    Flink系列文章 第01讲:Flink 的应用场景和架构模型 第02讲:Flink 入门程序 WordCount 和 SQL 实现 第03讲:Flink 的编程模型与其他框架比较 第04讲:Flin ...

  5. 「Flink」事件时间与水印

    我们先来以滚动时间窗口为例,来看一下窗口的几个时间参数与Flink流处理系统时间特性的关系. 获取窗口开始时间Flink源代码 获取窗口的开始时间为以下代码: org.apache.flink.str ...

  6. Flink流处理的时间窗口

    Flink流处理的时间窗口 对于流处理系统来说,流入的消息是无限的,所以对于聚合或是连接等操作,流处理系统需要对流入的消息进行分段,然后基于每一段数据进行聚合或是连接等操作. 消息的分段即称为窗口,流 ...

  7. Flink Streaming基于滚动窗口的事件时间分析

    使用flink-1.9.0进行的测试,在不同的并行度下,Flink对事件时间的处理逻辑不同.包括1.1在并行度为1的本地模式分析和1.2在多并行度的本地模式分析两部分.通过理论结合源码进行验证,得到具 ...

  8. Flink的时间类型和watermark机制

    一FlinkTime类型 有3类时间,分别是数据本身的产生时间.进入Flink系统的时间和被处理的时间,在Flink系统中的数据可以有三种时间属性: Event Time 是每条数据在其生产设备上发生 ...

  9. Flink架构(三)- 事件-时间(Event-Time)处理

    3. 事件-时间(Event-Time)处理 在“时间语义”中,我们强调了在流处理应用中时间语义的重要性,并解释了处理时间与事件时间的不同点.处理时间较好理解,因为它基于本地机器的时间,它产生的是有点 ...

随机推荐

  1. 【环境安装】Docker安装

    [环境安装]Docker安装 CentoOS-7 安装步骤: 1.卸载已经安装的Docker sudo yum remove docker \ docker-client \ docker-clien ...

  2. 【JMeter_11】JMeter逻辑控制器__Switch控制器<Switch Controller>

    Switch控制器<Switch Controller> 业务逻辑: 取得switch value的值,通过对节点下所有取样器.逻辑控制器的下标.名称匹配去执行,switch value的 ...

  3. Spring整合JDBC temple

    一.Spring对Jdbc的支持 Spring为了提供对Jdbc的支持,在Jdbc API的基础上封装了一套实现,以此建立一个 JDBC 存取框架. 作为 Spring JDBC 框架的核心, JDB ...

  4. cb50a_c++_STL_算法_局部排序partial_sort

    cb50a_c++_STL_算法_局部排序partial_sort partial_sort(b,se,e)排序一部分,begin,source end,endcout << " ...

  5. Vmaware克隆虚拟机后无法上网

    问题: 使用快照克隆了一台虚拟机 打开后发现无法上网,ifconfig查看状态 解决办法: 1.点击右下角的网络设置,点击设置,查看mac地址与文件/etc/udev/rules.d/70-persi ...

  6. 全链路监控系统开源Pinpoint入门视频教程(最新版本1.8)

    pinpoint支持的模块 源码:https://github.com/naver/pinpoint技术概述:https://skyao.gitbooks.io/learning-pinpoint/c ...

  7. 在maven项目中使用Junit进行单元测试(一)

    https://blog.csdn.net/ai_xue_xi/article/details/51819729 这篇文章相当的经典,最好使用的maven生成单元测试报告,不要在使用ant脚本生成单元 ...

  8. NET 数据结构-单链表

    概念介绍: 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素. 链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元 ...

  9. python设计模式之策略模式

    每次看到项目中存在大量的if else代码时,都会心生一丝不安全感. 特别是产品给的需求需要添加或者更改一种if条件时,生怕会因为自己的疏忽而使代码天崩地裂,哈哈,本文的目的就是来解决这种不安全感的, ...

  10. 问题记录--jekyll serve 启动的时候如何指定80端口

    jekyll serve --host 0.0.0.0 --port 80 启动失败