首页 > 解决方案 > Kafka Streams:处理来自不同分区的消息时的事件时间偏差

问题描述

让我们考虑一个具有多个分区和按事件时间顺序编写的消息的主题,而没有任何特定的分区方案。Kafka Streams 应用程序对这些消息进行一些转换,然后按某个键进行分组,然后通过具有给定宽限期的事件时间窗口聚合消息。

每个任务可以以不同的速度处理传入的消息(例如,因为在具有不同性能特征的服务器上运行)。这意味着在 groupBy shuffle 之后,当消息来自不同任务时,将不会在内部主题的同一分区中的消息之间保留事件时间排序。一段时间后,此事件时间偏差可能会变得大于宽限期,这将导致丢弃源自滞后任务的消息。

增加宽限期似乎不是一个有效的选项,因为它会延迟发出最终聚合结果。Apache Flink 通过在分区合并时发出最低水印来处理这个问题。

这应该是一个真正的问题,尤其是在处理大量历史数据时,还是我错过了什么?Kafka Streams 是否提供处理这种情况的解决方案?

更新我的问题不是关于 KStream-KStream 连接,而是关于单个 KStream 事件时间聚合,然后是流洗牌。

考虑这个代码片段:

stream
  .mapValues(...)
  .groupBy(...)
  .windowedBy(TimeWindows.of(Duration.ofSeconds(60)).grace(Duration.ofSeconds(10)))
  .aggregate(...)

我假设无论出于何种原因,某些任务的 mapValues() 操作可能会很慢,因此任务确实以不同的速度处理消息。当操作员发生 shuffle 时aggregate(),任务 0 可能已经处理了消息,t而任务 1 仍然在t-skew,但是来自两个任务的消息最终交错在内部主题的单个分区中(对应于分组键)。

我担心的是,当 skew 足够大(在我的示例中超过 10 秒)时,来自滞后任务 1 的消息将被丢弃。

标签: apache-kafkaapache-kafka-streams

解决方案


基本上,一个任务/处理器维护一个流时间,它被定义为已经轮询的任何记录的最高时间戳。然后,这个流时间在 Kafka Streams 中用于不同的目的(例如:Punctator、Windowded Aggregation 等)。

[窗口聚合]

正如您所提到的,流时间用于确定是否应接受记录,即 record_accepted = end_window_time(current record) + grace_period > observed stream_time

正如您所描述的,如果多个任务并行运行以基于分组键对消息进行混洗,并且某些任务比其他任务慢(或某些分区处于脱机状态),这将创建无序消息。不幸的是,恐怕解决这个问题的唯一方法就是增加grace_period.

这实际上是可用性和一致性之间的永恒权衡。

[ KafkaStream 和 KafkaStream/KTable Join 的行为

当您使用 Kafka Streams 执行连接操作时,内部任务将分配给多个共同分区主题的“相同”分区。例如,任务 0 将分配给 Topic1-Partition0 和 TopicB-Partition0。

提取的记录按分区缓冲到由任务管理的内部队列中。因此,每个队列都包含等待处理的单个分区的所有记录。

然后,从队列中逐一轮询记录并由拓扑实例处理。但是,这是来自非空队列的记录,具有从轮询返回的最低时间戳。

此外,如果队列为空,则任务可能会在一段时间内变得空闲,从而不再从队列中轮询记录。您实际上可以配置任务保持空闲的最长时间,可以使用流配置定义:max.task.idle.ms

这种机制允许同步共定位分区。Bu,默认max.task.idle.ms设置为 0。这意味着任务永远不会等待来自分区的更多数据,这可能导致记录被过滤,因为流时间可能会更快地增加。


推荐阅读