我是Kafka-stream的新手,我正在试验它来处理大量信息。
场景
传入的有效负载结构是:
"building-<M>, sensor-<N>.<parameter>, value, timestamp".
例如:
"building-1, sensor-1.temperature, 18, 2020-06-12T15:01:05Z"
"building-1, sensor-1.humidity, 75, 2020-06-12T15:01:05Z"
"building-1, sensor-2.temperature, 20, 2020-06-12T15:01:05Z"
"building-1, sensor-2.humidity, 70, 2020-06-12T15:01:05Z"
kafka中的消息密钥是building-id。
Stream将其转换为POJO,以进行进一步的下游处理:
SensorData {
buildingId = "building-1"
sensorId = "sensor-1"
parameterName = "temperature"
parameterValue = 18
timestamp = 1592048743000
..
..
}
每个传感器将同时发送其所有参数作为单独的记录。每组进料每隔5分钟从每个传感器发出一次。
时间戳提取器设置为从有效载荷中获取时间。如果记录上的时间戳距离很远(例如与当前流时间有1小时的偏差),它也会拒绝该记录。
在拓扑中,我想一次执行一项汇总操作,将来自一个传感器的所有数据组合在一起。例如,在上面的示例中,我想使用该传感器报告的温度和湿度为每个传感器执行汇总。
拓扑
我使用“ buildingId”和“ sensorId”进行分组,然后应用2分钟间隔和1分钟宽限期的会话窗口。
kStreamBuilder
.stream("building-sensor-updates", ...)
//Had to cleanup key and also needed some data from context
.tranform(() -> new String2SensorObjectConvertor())
//triggers another re-partition
.groupBy((key, value) -> value.buildingId + "-" + value.sensorId, ...)
.windowedBy(SessionWindows.with(..))
.aggregate(
() -> new SensorDataAggregator,
...,
Materialized.<String, SensorDataAggregator,
SessionStore<Bytes, byte[]>>as("session_aggregate_store"))
.suppress(Suppressed.untilWindowCloses(Suppressed.BufferConfig.unbounded()))
.toStream()
...
...
如预期的那样,这将触发重新分区,并且子流将使用此重新分区主题“ sensor_data_processor-session_aggregate_store-repartition”中的记录。我在那里看到了一个问题,稍后会解释。
测试输入数据
我正在测试一种方案,其中再次从存储或从Kafka偏移量重新处理过去的数据。为了进行测试,我使用Kafka-spool-connect从csv提供数据。输入的CSV文件中每个记录的时间戳按升序保留。对于同一传感器,下一组记录将使时间戳增加5分钟。
"building-1, sensor-1.temperature, 18, 2020-06-12T15:01:02Z"
"building-1, sensor-1.humidity, 75, 2020-06-12T15:01:05Z"
"building-1, sensor-2.temperature, 20, 2020-06-12T15:01:03Z"
"building-1, sensor-2.humidity, 70, 2020-06-12T15:01:06Z"
"building-1, sensor-1.temperature, 19, 2020-06-12T15:06:04Z"
"building-1, sensor-1.humidity, 65, 2020-06-12T15:06:08Z"
"building-1, sensor-2.temperature, 21, 2020-06-12T15:06:05Z"
"building-1, sensor-2.humidity, 73, 2020-06-12T15:06:09Z"
我批量注入测试数据(200000),没有任何延迟。
问题
当子流处理来自此重新分区主题的记录时,我看到来自KStreamSessionWindowAggregate的以下警告消息,并且记录被跳过。
警告 org.apache.kafka.streams.kstream.internals.KStreamSessionWindowAggregate -跳过过期窗口的记录。键= [建筑物ID-1003-传感器-1]主题= [传感器数据处理器_会话_聚合_存储分区] 分区= [0]偏移量= [1870]时间戳= [1591872043000] window = [1591872043000,1591872043000]到期时间= [1591951243000] streamTime = [1591951303000]
如果您查看警告消息中的时间戳记,
我尝试在7分钟的时间范围内提前2分钟进行尝试。我那里也有类似的问题。
观察
由于原始消息的关键字是“ building-id”,因此,来自同一建筑物(并因此属于同一传感器)的所有记录都应进入一个分区,并且来自每个传感器的记录应有序。
我还在拓扑的开头做tranform()。我不得不清理密钥,并且还希望从上下文中获取一些数据。尽管这可能会触发重新分区,但这不应更改传感器内的记录顺序,因为它仅清除键,因此分区结果将在分区中保持相同的元素。我将通过一些优化摆脱tranform()。
我的窗口分组基于building-id + sensor-id,因此每个重新分区的组中来自同一传感器的元素也应按顺序排列。
鉴于所有这些,我希望每个分区/组的流时间根据保持该分区顺序的事件的时间戳而单调递增。但我看到播放时间有所增加。我查看了 org.apache.kafka.streams.kstream.internals.KStreamSessionWindowAggregate 和一些kafka-stream文档-
在我看来,单调流时间是为流任务而不是按分区保持的。并且相同的流任务可以用于处理多个主题分区。由于记录是快速连续注入的,因此它可以处理来自一个分区的大量记录,并且当它选择另一个主题分区时,与新主题分区中记录的时间戳相比,流时间可能已经跨越了很多时间。将导致过期。
问题
对于重放这样的记录,除了在窗口上设置较大的宽限期外,如何处理它。
即使在实时情况下,如果存在背压也可能会发生此问题。不能使用较大的宽限期,因为使用Suppresed.untilWindowClose()会导致结果延迟。最好的方法是什么?
如果为流任务维护流时间,并且同一任务可以用于多个主题分区,那么是否还能在流任务和主题分区之间保持1-1映射和粘性?如果是这样,除了潜在的性能问题之外,还有什么其他含义?
为什么kafka-stream不会为主题分区而不是每个流任务维护流时间?
当我查看警告消息中提到的“ sensor_data_processor-session_aggregate_store-re-partition”主题时,我发现仅大部分“温度”记录都已发布到该主题(是的,对于每个组,测试数据集中首先显示“温度”。为什么只有温度记录才涉及该主题?只是时间巧合吗?
答案 0 :(得分:2)
对于重放这样的记录,除了在窗口上设置较大的宽限期外,如何处理它。
我想你不能。如果处理今天的数据以及昨天的以后数据,则昨天的数据将被丢弃。您可以执行以下操作来启动 new 应用程序。对于这种情况,启动时应用程序没有流时间,因此它将以“昨天”初始化其流时间,因此数据不会被丢弃。
即使在实时情况下,如果存在背压也可能会发生此问题。不能使用较大的宽限期,因为使用Suppresed.untilWindowClose()会导致结果延迟。处理此问题的最佳方法是什么?
嗯,您必须选择毒药...否则您将退回到Processor API并手动实现所需的任何逻辑。
如果为流任务维护流时间,并且同一任务可用于多个主题分区,那么我们是否还能在流任务和主题分区之间保持1-1映射和粘性?如果是这样,除了潜在的性能问题之外,还有什么其他含义?
每个任务肯定会保留流时间,并且任务和分区之间存在1:1的映射。也许数据被意外地拖曳了。 My window grouping is based on building-id + sensor-id, so the elements from same sensor in each re-partitioned group also should be coming in order.
:同意,但是数据仍然会被洗净;因此,如果一个上游任务处理数据的速度比其“并行”梨更快,那么如果所有下游任务也都将导致流时间的快速增长。
为什么kafka-stream不会为主题分区而不是每个流任务维护流时间?
不确定是否可以关注。每个任务分别跟踪流时间。任务与分区之间存在1:1的映射。因此,看起来(每个分区的跟踪或每个任务的跟踪-假设每个任务只有一个输入分区)都是相同的。