可以使用 Apache Kafka“无限保留策略”作为具有 CQRS 的事件源系统的基础吗?

Posted

技术标签:

【中文标题】可以使用 Apache Kafka“无限保留策略”作为具有 CQRS 的事件源系统的基础吗?【英文标题】:Is it ok to use Apache Kafka "infinite retention policy" as a base for an Event sourced system with CQRS? 【发布时间】:2020-03-04 22:03:36 【问题描述】:

我目前正在评估用于设计/实施事件溯源 + CQRS 体系结构方法的系统设计选项。由于我们想将 Apache Kafka 用于其他方面(普通发布-订阅消息传递 + 流处理),下一个合乎逻辑的问题是,“我们可以使用 Apache Kafka 存储作为 CQRS 的事件存储吗?”,或者更重要的是,这会是一个明智的决定吗?

现在我不确定这一点。 这个来源似乎支持它:https://www.confluent.io/blog/okay-store-data-apache-kafka/

此其他来源建议不要这样做:https://medium.com/serialized-io/apache-kafka-is-not-for-event-sourcing-81735c3cf5c

在我目前的测试/实验中,我遇到了类似于第二个来源所描述的问题,这些问题是:

    重组实体: Kafka 似乎不支持快速检索/搜索主题中的特定事件(例如:与订单历史相关的所有命令 - 似乎是重建实体实例所必需的要求扫描所有主题的事件并仅过滤与某些实体实例标识符匹配的事件,这是不行的)。 [这个其他人似乎也得出了类似的结论:Query Kafka topic for specific record——也就是说,这是不可能的(不依赖一些hacky技巧)] - 写入一致性: Kafka 不支持其存储的事务原子性,因此在将事件异步导出到 Kafka 之前,只使用一些锁定方法(通常是乐观锁定)放置一个 DB 似乎是一种常见的做法队列(不过我可以忍受,第一个问题对我来说更重要)。 分区问题: Kafka 文档中提到,“订单保证”仅存在于“Topic 的分区”内。同时他们也说分区是并行的基本单位,换句话说,如果你想并行工作,把消息分散到分区(当然还有经纪人)。但这是一个问题,因为事件源系统中的“事件存储”需要顺序保证,所以这意味着如果我绝对需要顺序保证,我不得不为此用例只使用 1 个分区。这是正确的吗?

尽管这个问题有点开放,但实际上是这样的:您是否使用 Kafka 作为事件源系统上的主要事件存储?您如何处理从命令历史记录中重组实体实例的问题(鉴于该主题有数百万个条目扫描所有集合不是一个选项)?您是否只使用了 1 个分区而牺牲了潜在的并发消费者(假设订单保证仅限于特定主题分区)?

我们将不胜感激任何具体或一般性的反馈,因为这是一个复杂的主题,需要考虑多个因素。

提前致谢。

编辑 6年前在这里有过类似的讨论: Using Kafka as a (CQRS) Eventstore. Good idea? 当时的共识也有分歧,很多人认为这种方法很方便,提到 Kafka 是如何原生处理大量实时数据的。然而,问题(至少对我而言)与此无关,而是与 Kafka 重建实体状态的能力有多不方便有关 - 通过将主题建模为实体实例(不希望主题数量呈指数增长) ,或者通过对主题和实体类型进行建模(主题中的事件数量使得重建非常缓慢/不切实际)。

【问题讨论】:

【参考方案1】:

您的理解基本正确:

    kafka 没有搜索。绝对不是关键。有一个时间戳,但它不完美,不适合您尝试做的事情。 如今,kafka 实际上支持有限形式的事务(仅查看一次),尽管如果您与 kafka 之外的任何其他系统进行交互,它们将毫无用处。 kafka 中任何事物的单元(事件排序、可用性、复制)都是一个分区。不能保证跨同一主题的分区。

所有这些都不会阻止应用程序使用 kafka 作为其状态的真实来源,只要:

    您的问题可以“分片”到主题分区中,因此您不必关心跨分区的事件顺序 如果/当您丢失本地状态作为引导程序时,您愿意“重播”整个分区。 您使用日志压缩主题来尝试限制其大小(因为您需要在引导程序中重播它们,请参见上文)

samza 和 (IIUC) kafka-streams 都使用日志压缩的 kafka 主题返回其状态存储。在内部到 kafka 偏移和消费者组管理存储为日志压缩主题,代理在内存中持有“物化视图” - 当__consumer_offsets 分区的所有权在代理之间移动时,新领导者重播分区以重建此视图。

【讨论】:

你好,radai,很棒的输入,我觉得它很有帮助!您关于将问题“分片”到主题分区的建议是我之前没有考虑过的,可以解决“并发消费者问题”。我对日志压缩主题 (towardsdatascience.com/…) 的了解是,它每个键只维护 1 个事件(最新的),这对我没有多大帮助,因为实例的历史将由它的所有事件来描述,而不仅仅是最后一个。我一般我相信我可以采用混合方法。感谢您的详细回答。 当最新事件(键)以某种方式聚合其历史数据(类似于聚合实例当前状态)时,或者当我只对“最新值”感兴趣时,日志压缩主题似乎非常有用“当前价格”或“当前转换率”。对于单个事件/命令仅描述一个小的“状态突变”并且同时我们想要所有这些历史突变的事件溯源,日志压缩主题似乎对此无济于事(除非我忽略了一些重要的事情是)。 您提到的“如果/当您失去本地状态时愿意“重播”整个分区”。我相信我们可以这样做来投影新的视图模型/查询模型,至于“主视图模型”,我们必须根据具体情况来解决。再次,很好的反馈,谢谢。 日志压缩主题通常​​在人们只关心每个键的最后一个值时使用 - 例如,对于维护某些 KV 存储更改日志的人来说。 见infoq.com/news/2018/07/event-sourcing-kafka-streams 和链接的谈话(我没看过,但听起来像我在说什么)。对于您之前的问题:是的,完全正确。

以上是关于可以使用 Apache Kafka“无限保留策略”作为具有 CQRS 的事件源系统的基础吗?的主要内容,如果未能解决你的问题,请参考以下文章

Apache Apex - Kafka 0.9 安全 kafka 主题

Apache Kafka 中的事件外包

使用 Apache Camel Source 从 S3 到 Kafka

Apache Kafka 简介与使用

如何监控 Apache Kafka 指标?

10 Apache Kafka补充