使用Kafka KStream through()方法-了解实例对分区的分配

时间:2019-02-22 22:41:38

标签: apache-kafka apache-kafka-streams

使用KStream through()方法来确保将消息分发到正确的分区时遇到了麻烦。

这里是一些背景。我有一个kafka流应用程序,它在inTopic上侦听CustomerEvent并在outTopic上编写CatalogEvent:

inTopic ---> MY_KAFKA_STREAM_APPLICATION ---> outTopic
  • 在inTopic上,键和值是(AccountId,CustomerEvent)。
  • 在outTopic上,键和值是(CatalogId,CatalogEvent)

我正在使用KStream transform()方法将CustomerEvent转换为CatalogEvent。我需要使用transform(),因为CatalogEvent依赖于以前的CustomerEvent,它们共享与我过去看到的相同的CatalogId,因此将涉及一个状态存储。

这是我初始化状态存储的方式。我将通过CatalogId查询状态存储,以检索有关以前CustomerEvent的信息,这些信息共享与我以前见过的相同的CatalogId。

StoreBuilder<KeyValueStore<String, MyAggregator>> catalogStore = 
     Stores.keyValueStoreBuilder(Stores.persistentKeyValueStore("myStore"), Serdes.String(), aggregatorSerde)
           .withLoggingEnabled(new HashMap<>());

builder.addStateStore(catalogStore);

这是我设置拓扑的方式:

builder.stream("inTopic", Consumed.with(Serdes.String(), customerEventSerde))
    .selectKey((k, customerEvent) -> customerEvent.getCatalogId())
    .through("bycatalogid", Produced.with(Serdes.String(), customerEventSerde))
    .transform(()-> new MyTransformer("myStore"), "myStore")
    .to("outTopic", Produced.with(Serdes.String(), catalogEventSerde));

我需要确保共享相同CatalogId的所有CustomerEvent都位于同一分区上。因此,这就是为什么我使用selectKey()将密钥从AccountId更改为CatalogId并使用through()方法的原因。

我正在针对主题进行2个分区,2个kafka流应用程序实例和1个kafka服务器的测试。

我正在使用以下命令来查看如何将实例分配给每个分区:

良好的分配情况

kafka-consumer-groups.sh --describe --group my_application_group --bootstrap-server 192.168.92.118:9092
Note: This will not show information about old Zookeeper-based consumers.


TOPIC          PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG CONSUMER-ID
bycatalogid    1          -               0               -   consumer1/192.168.92.118 
inTopic        1          9               9               0   consumer1/192.168.92.118
bycatalogid    0          5               5               0   consumer2/192.168.92.29
inTopic        0          12              12              0   consumer2/192.168.92.29

有时候,分配很好,如上所示。正确分配了所有使用者。在192.168.92.118上运行的实例分配给partition1,在192.168.92.29上运行的实例分配给partition0。另外,我看到所有共享相同CatalogId的CustomerEvents被发送到同一分区。

但是,有时当我重新启动实例时,将实例分配给分区是错误的:

错误的分配案例

kafka-consumer-groups.sh --describe --group my_application_group --bootstrap-server 192.168.92.118:9092
Note: This will not show information about old Zookeeper-based consumers.

TOPIC          PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG CONSUMER-ID
bycatalogid    0          11              11              0   consumer1/192.168.92.118
bycatalogid    1          3               3               0   consumer1/192.168.92.118
inTopic        0          18              18              0   consumer2/192.168.92.29
inTopic        1          12              12              0   consumer2/192.168.92.29

以上分配完全没有意义。在192.168.92.118上运行的实例仅在主题bycatalogid上侦听,而在192.168.92.29上的另一个实例仅在主题inTopic上侦听。那怎么可能?

此外,出于调试目的,我在服务中实现了REST api,可以在其中发送带有CatalogId的HTTP GET请求以检索kafka存储中的内容。我正在这样访问我的kafka商店:

ReadOnlyKeyValueStore<String, MyAggregator> catalogStore 
    = streams.store("myStore", QueryableStoreTypes.<String, MyAggregator>keyValueStore());

如果仅在inTopic分区上侦听的实例上执行,则上面的语句将引发以下异常。

Caused by: org.apache.kafka.streams.errors.InvalidStateStoreException: The state store, myStore, may have migrated to another instance.

我需要做些什么来确保我没有收到不良分配案例

谢谢。

1 个答案:

答案 0 :(得分:0)

两种情况均有效。您的输入主题和通过主题都有2个分区。因此,创建了4个任务,每个任务是一个独立的处理单元,可以独立于所有其他任务进行分配。

没有理由为什么一个实例需要处理来自两个不同主题的分区。

对于交互式查询:上面是可将存储托管在不同实例上的原因。 (参见https://kafka.apache.org/21/documentation/streams/developer-guide/interactive-queries.html