Kafka Stream和Kafka表一对多的关系加入

时间:2017-05-31 03:50:10

标签: apache-kafka apache-kafka-streams

我有一个kafka流 - 比如博客和kafka表 - 请注意与这些博客相关的评论。来自kafka流的密钥可以映射到Kafka表中的多个值,即一个博客可以有多个注释。我想要连接这两个并使用注释id数组创建一个新对象。但是当我进行连接时,流只包含最后一个注释ID。是否有任何文档或示例代码可以指出我正确的方向如何实现这一目标?基本上,是否有任何文档阐述如何使用Kafka流和Kafka表进行一对多关系连接?

KStream<Integer, EnrichedBlog> joinedBlogComments = blogsStream.join(commentsTbl,
              (blogId, blog) -> blog.getBlogId(),
              (blog, comment) -> new EnrichedBlog(blog, comment));

因此,我需要有一系列评论ID,而不是评论。

3 个答案:

答案 0 :(得分:4)

我在代码示例中找不到具有匹配签名的连接方法,但这就是我认为的问题:

KTables被解释为changlog,也就是说,具有相同键的每个下一个消息都被解释为对记录的更新,而不是新记录。这就是为什么您只看到给定键(博客ID)的最后一条“评论”消息,之前的值被覆盖。 要解决这个问题,您首先需要更改填充KTable的方式。您可以做的是将您的评论主题作为KStream添加到拓扑中,然后执行聚合,该聚合只是构建一个数组或共享相同博客ID的注释列表。该聚合返回一个KTable,您可以将其加入您的博客KStream。

以下是如何构建List-valued KTable的草图:

builder.stream("yourCommentTopic") // where key is blog id
.groupByKey()
.aggregate(() -> new ArrayList(), 
    (key, value, agg) -> new KeyValue<>(key, agg.add(value)),
    yourListSerde);

列表在聚合中比在数组中更容易使用,因此我建议您在需要时将其转换为下游数组。您还需要为列表提供serde实现,在上面的示例中为“yourListSerde”。

答案 1 :(得分:2)

如果您将avro与架构注册表一起使用,则应编写自己的聚合器,因为kafka流无法序列化ArrayList。

<button (click)="invokeFun()">Link</button>

然后将invokeFun(){ window.location.href = 'mylink.com?state=take-back-to-my-link.com'; } 与其他视频流( val kTable = aStream .groupByKey() .aggregate( { YourAggregator() // initialize aggregator }, { _, value, agg -> agg.add(value) // add value to a list in YourAggregator agg } ) )加入。

kTable

对不起,我的摘要是用Kotlin写的。

答案 2 :(得分:1)

正如上面 Michal 的正确答案所指出的那样,在这种情况下,不能使用以 KTable 为键的 blogId 来跟踪博客,因为此类表中仅保留最新的博客值.

作为对他的回答中提到的解决方案的建议优化,请注意,如果每个博客有大量评论,则在 .aggregate() 中保留不断增长的列表可能会在数据大小和时间方面造成代价。这是因为在幕后,该聚合的每次迭代都会导致 List 的实例不断增长,由于数据重用,这在 java 或 scala 中是可以的,但是每个实例都单独序列化到底层状态-店铺。示意性地,假设某个键已经说了 10 条评论,那么这个表达式被调用了 10 次:

(key, value, agg) -> new KeyValue<>(key, agg.add(value))

每次生成一个大小为 1,然后是 2,然后......然后是 10 的列表,每个都独立地序列化到引擎盖下的状态存储,这意味着 1+2+3+...+10=55 值将总共被序列化(好吧,也许有一些优化,其中一些序列化被跳过了,我不知道,虽然我认为空间和时间复杂度是一样的)。

另一种虽然更复杂的方法是在状态存储中使用 range scans,这使得数据结构看起来有点像 DynamoDB 等键值存储中的 (partition_key, sort_key),我们在其中存储每个使用 (blogId, commentId) 之类的键进行注释。在这种情况下,您仍然会通过 keyBy() blogId 评论流,然后 .transform(...) 将其传递给处理器 API,您可以在其中应用范围扫描的想法,每次添加(即序列化)对状态存储的单个补充注释,而不是整个列表的新实例。

当我们描绘很多 (blogId, commentId) 键的实例时,一对多关系变得非常明显,所有实例都具有相同的 blogId 和不同的 commentId,所有这些都存储在相同的状态存储实例在同一个物理节点中,这整个事情在很多节点中并行发生在很多 blogId 上。

我在我的博客中添加了有关该模式的更多详细信息:One-to-many Kafka Streams Ktable join,并添加了 full working example in github