使用KStream through()
方法来确保将消息分发到正确的分区时遇到了麻烦。
这里是一些背景。我有一个kafka流应用程序,它在inTopic上侦听CustomerEvent并在outTopic上编写CatalogEvent:
inTopic ---> MY_KAFKA_STREAM_APPLICATION ---> outTopic
我正在使用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.
我需要做些什么来确保我没有收到不良分配案例。
谢谢。
答案 0 :(得分:0)
两种情况均有效。您的输入主题和通过主题都有2个分区。因此,创建了4个任务,每个任务是一个独立的处理单元,可以独立于所有其他任务进行分配。
没有理由为什么一个实例需要处理来自两个不同主题的分区。
对于交互式查询:上面是可将存储托管在不同实例上的原因。 (参见https://kafka.apache.org/21/documentation/streams/developer-guide/interactive-queries.html)