• 为什么 Flink 支持几种不同的时间概念?
  • 什么是Watermarks,它们解决了什么问题?
  • 什么是迟到的事件,我能用它们做什么?

Flink中不同的时间概念

image.png

  • Event Time
    • 事件时间是事件实际发生的时间。事件消息/记录中通常带有由检测或产生事件的边缘设备记录的事件发生时间戳
    • 处理结果的确定性以及重新处理历史数据的能力是使用event time的主要优势
  • Storage Time
    • 存储时间是存储层或消息代理接收事件的时间。这可能是事件第一次由具有可靠时间戳的、管理良好的基础设施处理
    • 存储时间不是 Flink API 中的概念。这是我们编造的一个术语,基于此可以讨论存储层可能提供的(事件时间)时间戳。例如,Kafka 代理可以将时间戳添加到他们正在处理的数据中。在不能信任边缘设备提供有意义的时间戳的情况下,可以使用这种机制。
    • 从 Flink 的 视角来看,它不区分事件时间和存储时间时间戳——这些都只是事件被读入 Flink 时已经存在的时间戳。
  • Ingestion Time
    • 可以将 Flink 应用程序配置为在事件刚到达Flink时的时间戳。对于应用程序的其余部分,这些被看做是事件时间戳(但它们不可重现)
  • Processing Time
    • 事件的处理时间是系统时钟时间。虽然事件的event time是确定的和不可变的,但处理时间总是在变化
      • 如果在同一数据上多次运行基于event time的data pipeline,将获得相同的结果输出;处理结果的确定性以及重新处理历史数据的能力是使用event time的主要优势
      • 如果在同一数据上多次运行基于processing time的data pipeline,将可能获得不同的结果输出

无序事件流

  1. 假设有一个事件流

image.png

  • 使用事件时间的这些优势确实是以增加一些复杂性为代价的。
  • 当完全在process time内操作时,就时间而言,没有什么是无序的。但是一旦开始对上游时间戳的事件执行分布式操作,那么就需要准备好处理乱序事件流。
  • 上图为一条从左边到达的流,它只是非常粗略地按顺序排列的。事件的时间戳(以秒为单位)显示在方框中
  1. 假设我们的目标是以10s为单位的窗口中计算事件出现的次数。

image.png

  1. 前5个事件已经被准确的放到他们应该去的窗口中

image.png
目前来看,似乎0-10s这个窗口已经没有数据进来了,我们如何判断0-10s这个窗口已经计算完了,可以输出了?当然我们可以从一个上帝视角知道事件6还会进第1个窗口,但是对于流计算处理而言,只能根据当前接收到的事件来做决策。

最终,我们的窗口计数应用程序将不得不停止等待并为第一个窗口产生一些结果。有2种选择:一种是等待更长的时间,这样这个窗口包含完整数据的概率更高,一种是不怎么等,快速产生结果,这种窗口数据不完整的概率更高。

作为一名应用开发,需要在完整性和延迟性之间权衡。watermarks就是Flink为解决这一问题提供的机制工具。

Watermarks

image.png

  • Watermarks由数据源和应用来提供
  • 他们是stream的一部分,并且携带时间戳。也就是说Watermark是一种StreamElement,其有效负载只是一个long型的时间戳。应用的事件被打包成StreamRecords,另一种StreamElement。
  • Watermark断言所有较早的事件已经(可能)到达
  • 上图显示了一些已插入流中的watermark

Flink 有两种内部时钟,一种标记event time的流向,另一种连接到处理时间。处理时间时钟自动向前移动。事件时钟连接到数据流中的watermark,并且只有在watermark到达时才会提前。

Flink 应用程序开发人员,负责通过在流中安排watermark来将事件时间向前移动。

有限的无序

image.png

  • 上图展示了最简单的一种watermarks生成方法,即假设流里无序事件不超过6s。
  • watermark不是自动的。如果在 Flink 作业中使用事件时间处理,那么必须安排提供watermark。在大多数情况下,最简单的解决方案是扩展 Flink 的有界乱序watermark生成器,它假设事件以一定的延迟量到达。
  • 在上面的图例中,我们假设事件乱序不超过6s,依此来设置watermark。当我们看到时间戳是21的事件到达时,我们推断15=(21-6)之前的事件全部已经到达,插入一个watermark为15的stream event。
  • 尽管紧跟watermark(15)后,我们看见有一个6到达。我们后面会讨论如何处理这些晚到的事件,当前仅需关注Flink的watermark机制。更多关于BoundedOutOfOrdernessWatermarks请查看the docs

Watermarks支持事件时间计时器

  • Processing time timer:8点叫醒我
  • Event time timer:当你发现8点前的任何事件时,叫醒我。

计时器触发机制

  • Processing time timer:系统时钟达到设定的时间
  • Event time timer:当前的watermark达到设定的时间

计时器可以用来

  • 触发基于时间的窗口
  • 等待乱序自行解决
  • 清理不再需要的状态

当前的watermark:watermarks是单调递增的。每个算子都维护其当前watermark的本地概念,即本地事件时钟的时间。当前的watermark只有在新的、更大的watermark到达时才能前进。

举个例子:我们需要处理来自电商的事件流。每当发生购买行为事件时,希望将导致购买的所有用户事件打包在一起,并使用该客户旅程来训练机器学习模型。应用不能在购买事件一到时就执行该操作,而是需要等到更早的乱序事件也可用,这样客户旅程是完整。也就是说应用实际上是等到watermark赶上购买事件中的时间戳才开始真正触发处理。

让我们再回到开始的例子中,在10s的窗口计数。每个窗口有一个相关联的事件时间计时器。当流的当前watermark大于或等于 10 时,第一个计时器将触发。
image.png
image.png
当watermark(15)到达窗口算子时,第一个窗口将会被触发输出以现在窗口能看见的4个event的结果。

迟到/晚到

image.png

  • 使用事件时间时,事件不仅会乱序到达,而且可能会迟到。当当前event timestamp < 当前watermark,我们认为是迟到/晚到。如上图图示中的时间6(<15)
  • 应用程序定义处理延迟事件的策略
  • 注意:允许无限延迟通常需要保持无限状态。一般来说,不可能容纳任意延迟的事件,因为这需要保持不断增加的状态量。相反,最佳实践是适应一定数量的延迟,然后使保留的任何状态最终过期并释放。

允许迟到和窗口状态

image.png

  • 在这个例子中,我们不会永远保留第1个 10 秒的窗口,事实上 Flink 的窗口 API 在设计上也规避这种情况发生。
  • Flink 的事件时间窗口算子有一个允许的延迟参数,默认为零。这个允许的延迟定义了窗口初始触发后的时间间隔,在此期间窗口的状态被保留,延迟事件可以触发额外的窗口触发。如下图所示。

image.png

有状态的时间处理生命周期

image.png
上图展示了有状态流应用程序遵循的一般模式。由于事件无序到达,因此通常无法在事件到达时立即采取行动。相反,如果需要等到任何较早的事件到达,则可以通过设置一个事件时间计时器来完成,该计时器将在当前watermark达到相关事件的时间戳时触发。同时,可以记录有关事件的一些状态,以在计时器触发和执行操作时使用。

如果迟到的事件也需要访问记录的状态信息,则使用第二个计时器来定义另一个间隔,在这间隔之后状态将被释放。否则,可以在执行操作后立即释放状态。

总结

  • Flink 支持不同的时间概念
  • 采用event time时
    • events可能是乱序的
    • 对应用而言,重复执行回放数据,结果是确定的
    • 应用可以基于同样的代码处理历史和活跃数据
  • 基于event time的应用需要在应用中处理
    • 提供watermarks
    • 决策如何处理迟到事件
  • 流计算应用需要权衡完整性和延迟性
    • 等待事件长,获取完整数据的概率高
    • 等待时间短,延迟低
    • watermarks是有效处理这种权衡的机制

完整性和延迟之间的权衡可以获得提升,从以下2方面:

  • 严格的watermark导致早出结果
  • 允许延迟能用更完整的事件修正之前触发的结果

此外,应用必须处理由此带来的情况:每个事件可能会产生2个结果。