提问者:小点点

如何在GlobalKTable上执行“反向查找”?


我使用kafka已经有一段时间了,但我对Kafka StreamsAPI和一般流处理的概念相当陌生。

想象一个kafka流应用程序,它通过使用一个由附加kafka主题提供的查找表来执行编码和解码。业务主题都是共同分区的,有5个分区。查找映射流是一个更改数据流——它是压缩的,只有一个分区。

+------------------------+        /-------\       +--------------------------+
| "Plain request" stream |   ->  | Encoder |  ->  | "Encoded request" stream |     -----+
+------------------------+        \-------/       +--------------------------+          |
                                                                                        |
                                      ^                                                 |
                                      |                                                 v
+-------------------------+
| "Lookup mapping" stream |  ->    (Cache)                               (Some other app)
+-------------------------+
                                      |                                                 |
                                      v                                                 |
                                                                                        |
+-------------------------+       /-------\       +---------------------------+         |
| "Plain response" stream |  <-  | Decoder |  <-  | "Encoded response" stream |     <---+
+-------------------------+       \-------/       +---------------------------+

让我们假设编码是ASCII('a'-

我目前的草案是在GlobalKTable上使用LeftJoin(它实现了Lookup映射流):

final var mappingTable = streamsBuilder.globalTable(LOOKUP_MAPPINGS, ...);

streamsBuilder.stream(PLAIN_REQUESTS, ...)
  .leftJoin(mappingTable, (key, value) -> value.getPlain(), EncoderHelper::encode)
  .to(ENCODED_REQUESTS, ...);

编码工作符合预期。但是解码呢?

物化的GlobalKTable包含如下数据:

对于编码,LeftJoin操作可以根据普通值执行查找——该值用作表的键。但是对于解码,LeftJoin操作需要根据不是表中键的编码值(即97)执行查找。

我不知道解码应该如何设计。

通过kafka消费者API消费主题,存储数据并“手动”执行编码/解码。这可行,但有一些陷阱(需要确保在流处理开始之前读取所有数据)。我不太喜欢这个解决方案,因为它感觉像重新发明轮子。

我意识到我可以创建另一个主题(在我的程序之外),它使用编码值作为键,然后复制上面的解决方案。但这意味着两个强耦合的主题,感觉通常是错误的。

在将主题具体化为GlobalKTable之前,我还考虑过在我的程序中重新划分主题。基本上与之前的概念相同,但这一次,额外的主题只需要存在于我的程序中。这仍然会导致重复映射,因此增加内存占用。我可以接受,因为我的用例中只有少量映射(大约1000个)。这仍然感觉不是最佳的(我也不认为它是GlobalKTable的预期用例)。

我可以重新分区并使用KTable。但是由于共同分区的要求,这看起来很复杂,而且感觉也不对。

我正在寻找这样一个双向查找系统如何根据kafka流API设计原则实现的提示。更有经验的流人将如何解决这个问题?

相似但不相同:

使用kafka流的数据浓缩,KStream-GlobalKtable Join:想象一下,我想在他们的场景中从"john"XDFER进行查找。

KStream通过非键值与GlobalKTable连接:纯值('a')不存在于编码的响应流中。所以它不能用作键。


共1个答案

匿名用户

使用DSL,您已经提供了答案。您需要两个主题和两个全局表。

如果您愿意接受一些每次点击,您可以回退到处理器API并使用“全局存储”(它与全局表基本相同,但API更灵活)。而不是使用join()你实现了一个自定义的处理器(或变压器;取决于你使用的Kafka Streams的版本),在process()函数中,你可以对全局存储进行全表扫描(而不仅仅是键查找)以找到正确的条目。--鉴于你没有存储大量数据,你甚至可以在内存中缓存一个反向映射以避免一直扫描整个存储(当然,如果全局存储得到更新,你会得到缓存失效问题…)