我发现了两个问题,如果分区中没有新记录,为什么不发出结果记录:
1. "Kafka Stream Suppress session-windowed-aggregation"和
2. "Kafka Streams (Suppress): Closing a TimeWindow by timeout"
在回答这两个问题时,解释是必须发送新记录才能发出记录。
我不明白,为什么在超时后发出没有新记录的记录会违反禁止合同,并希望得到解释。
到目前为止,最好的建议是使用虚拟记录来触发排放。
我认为关闭和重新启动流(拓扑)可能比编写伪记录更合适。我认为流的新实例将使记录达到最高峰,并在超时到期后发出结果。
但是,我尝试并发现它不起作用。如果可以的话,请您解释一下。
@Slf4j
public class KafkaStreamVerticle extends AbstractVerticle {
private KafkaStreams streams;
@Override
public void start(Future<Void> startFuture) throws Exception {
Single.fromCallable(() -> getStreamConfiguration()).subscribe(config -> {
final StreamsBuilder builder = new StreamsBuilder();
builder.<String, String>stream(KafkaProducerVerticle.TOPIC)
.flatMapValues((k, v) -> List.<JsonObject>of(new JsonObject(v).put("origKey", k)))
.selectKey((k, v) -> v.getString(KafkaProducerVerticle.CATEGORY))
.flatMapValues(v -> List.<String>of(v.toString()))
.groupByKey(Grouped.with(Serdes.String(), Serdes.String()))
.windowedBy(TimeWindows.of(Duration.ofSeconds(4)).grace(Duration.ZERO)).count()
// .suppress(Suppressed.untilWindowCloses(BufferConfig.unbounded())).toStream().foreach((k,
.suppress(Suppressed.untilTimeLimit(Duration.ofSeconds(4), BufferConfig.unbounded()))
.toStream().foreach((k, v) -> log.info("********* {}: {} - {}: {}", k.key(),
k.window().start(), k.window().end(), v));
streams = buildAndStartsNewStreamsInstance(config, builder);
Runtime.getRuntime().addShutdownHook(new Thread(streams::close));
restartStreamsPeriodicaly(config, builder, 30_000L);
log.info("consumer deployed");
startFuture.complete();
});
}
private KafkaStreams buildAndStartsNewStreamsInstance(Properties config,
final StreamsBuilder builder) {
KafkaStreams streams = new KafkaStreams(builder.build(), config);
streams.cleanUp();
streams.start();
return streams;
}
private void restartStreamsPeriodicaly(Properties config, final StreamsBuilder builder,
@NonNull Long period) {
vertx.setPeriodic(period, l -> {
log.info("restarting streams!!");
streams.close();
streams = buildAndStartsNewStreamsInstance(config, builder);
});
}
private Properties getStreamConfiguration() {
final Properties streamsConfiguration = new Properties();
streamsConfiguration.put(StreamsConfig.APPLICATION_ID_CONFIG, "suppress-example");
streamsConfiguration.put(StreamsConfig.CLIENT_ID_CONFIG, "suppress-client");
streamsConfiguration.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
streamsConfiguration.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG,
Serdes.String().getClass().getName());
streamsConfiguration.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG,
Serdes.String().getClass().getName());
streamsConfiguration.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
streamsConfiguration.put(StreamsConfig.STATE_DIR_CONFIG, "/tmp/kafka-streams");
streamsConfiguration.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 10);
streamsConfiguration.put(StreamsConfig.CACHE_MAX_BYTES_BUFFERING_CONFIG, 0L);
return streamsConfiguration;
}
}
答案 0 :(得分:0)
Kafka Stream提供事件时间语义,这意味着,它的内部时间仅基于记录的时间戳而提前(内部时间从不基于壁钟时间提前)。您正在执行的“超时”,它也基于事件时间(而不是挂钟时间)。
假设您有一个大小为5的窗口(即[0,5)
是一个窗口),并且看到的数据为ts = 1,2,3。这意味着下一条记录可能具有timestamp = 4,并且必须包含在窗口中。但是,如果没有新数据到达,则无论等待多长时间,都不会发出窗口结果。仅当timestamp = 5的记录到达时,内部时间才会前进,并且现在大于窗口结束时间,并且会发出窗口结果。如果prevent()将在基于墙上时钟的超时后发出数据,并且下一条记录的时间戳为4,则它将发出错误的结果。
此外,prevent()会记住其内部状态和时间。因此,即使您重新启动应用程序,prevent()仍将缓冲数据,并仍将等待timestamp = 5的记录来发出数据。