聚合Kafka Streams中的多个分区

时间:2018-06-03 19:53:35

标签: apache-kafka apache-kafka-streams

这部分是Aggregation over a specific partition in Apache Kafka Streams

的后续行动

假设我有一个名为“events”的主题,其中包含3个分区,我发送字符串 - >像这样的整数数据:

分区1上的

(Bob,3)

分区2上的

(Sally,4)

分区3上的

(Bob,2)

...

我想在所有分区中聚合值(在此示例中,只是一个简单的总和),最终得到KTable,如下所示:

(莎莉,4)

(Bob,5)

正如我在上面链接的问题的答案中所提到的,直接进行这种跨分区聚合是不可能的。但是,回答者提到如果消息具有相同的密钥(在这种情况下也是如此)是可能的。怎么可以实现呢?

我还希望能够从跨Kafka Streams应用程序的每个实例复制的“全局”状态存储中查询这些聚合值。

我的第一个想法是使用GlobalKTable(我相信,根据this page,应该是我需要的)。但是,此状态存储的changelog主题与原始“events”主题具有相同数量的分区,并且仅基于每个分区而不是跨所有分区进行聚合。

这是我的应用程序的精简版 - 不确定从哪里开始:

final Properties streamsConfig = new Properties();
streamsConfig.put(StreamsConfig.APPLICATION_ID_CONFIG, "metrics-aggregator");
streamsConfig.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
streamsConfig.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
streamsConfig.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, CustomDoubleSerde.class.getName());
streamsConfig.put(StreamsConfig.producerPrefix(ProducerConfig.LINGER_MS_CONFIG), 0);
streamsConfig.put(StreamsConfig.NUM_STREAM_THREADS_CONFIG, 1);

final StreamsBuilder builder = new StreamsBuilder();

KStream<String, Double> eventStream = builder.stream(INCOMING_EVENTS_TOPIC);
KTable<String, Double> aggregatedMetrics = eventStream
        .groupByKey()
        .aggregate(() -> 0d, (key, value, aggregate) -> value + aggregate);

aggregatedMetrics.toStream().print(Printed.<String, Double>toSysOut());
aggregatedMetrics.toStream().to(METRIC_CHANGES_TOPIC);

final KafkaStreams streams = new KafkaStreams(builder.build(), streamsConfig);
streams.cleanUp();
streams.start();

builder.globalTable(METRIC_CHANGES_TOPIC, Materialized.<String, Double, KeyValueStore<Bytes, byte[]>>as(METRICS_STORE_NAME));

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    streams.close();
}));

1 个答案:

答案 0 :(得分:5)

Kafka Streams假设输入主题按键分区。这种假设并不适用于您的情况。因此,您需要告诉Kafka Streams。

在您的特定情况下,您会将groupByKey替换为groupBy()

KTable<String, Double> aggregatedMetrics = eventStream
    .groupBy((k,v) -> k)
    .aggregate(() -> 0d, (key, value, aggregate) -> value + aggregate);

lambda是一个不修改密钥的虚拟对象,但是,这是一个提示Kafka Streams在进行聚合之前根据密钥重新分区数据。

关于GlobalKTable:这是一种特殊的表,它不是聚合的结果,而只是从changelog主题填充。您的代码似乎已经做了正确的事情:将聚合结果写入主题并重新阅读主题为GlobalKTable