使用 kafka 和 cassandra 进行事件溯源的类别预测

Category projections using kafka and cassandra for event-sourcing

我正在使用 Cassandra 和 Kafka 进行事件溯源,而且效果很好。但我最近才发现 design/set-up 中的一个潜在重大缺陷。简要介绍如何完成:

  1. 聚合命令处理程序基本上是一个 kafka 消费者,它消费对某个主题感兴趣的消息:

    1.1 当它收到命令时,它会加载聚合的所有事件,并为每个事件重放聚合事件处理程序以使聚合达到当前状态。

    1.2 然后根据命令和业务逻辑将一个或多个事件应用到事件存储。这涉及将新事件插入 cassandra 中的事件存储 table。这些事件带有聚合的版本号标记 - 从版本 0 开始用于新聚合,从而使预测成为可能。此外,它将事件发送到另一个主题(用于投影目的)。

    1.3 kafka 消费者将在这些事件发布后监听该主题。该消费者将充当投影仪。当它收到感兴趣的事件时,它会加载聚合的当前读取模型。它检查它收到的事件版本是否是预期版本,然后更新读取模型。

这似乎很有效。问题是当我想要 EventStore 所谓的类别预测时。我们以订单聚合为例。我可以轻松地投影一个或多个阅读模型 pr Order。但是,如果我想要一个包含客户最后 30 个订单的投影,那么我将需要一个类别投影。

我只是在摸不着头脑,不知道如何做到这一点。我很想知道是否还有其他人在使用 Cassandra 和 Kafka 进行事件溯源。我读过几个地方,有些人不鼓励它。也许这就是原因。

我知道 EventStore 内置了对此的支持。也许使用 Kafka 作为事件存储会是更好的解决方案。

对于这种架构,您必须做出选择:

  • 每种类型的全局事件流 - 简单
  • 按类型划分的事件流 - 可扩展

除非您的系统具有相当高的吞吐量(比如在持续时间段内每秒至少有 10s 或 100s 事件到相关流类型),否则全局流是更简单的方法。一些系统(例如 Event Store)通过具有非常细粒度的流(例如每个聚合实例)但能够将它们组合成更大的流(每个流 type/category/partition,每个多种流类型等)以开箱即用的高性能和可预测方式,同时仍然很简单,只需要您跟踪单个全局事件位置。

如果你使用 Kafka 进行分区:

  • 在处理需要进入相同模型的不同分区的事件时,您的投影代码将需要处理访问相同读取模型的并发消费者组。根据您的投影目标商店,有很多方法可以处理此问题(事务、乐观并发、原子操作等),但对于某些目标商店来说这将是一个问题
  • 您的投影代码需要跟踪每个分区的流位置,而不仅仅是单个位置。如果您的投影从多个流中读取,它必须跟踪很多位置。

使用全局流消除了这两个问题 - 性能通常可能足够好。

在任何一种情况下,您可能还希望将流位置放入长期事件存储(即 Cassandra)中 - 您可以通过从事件流(分区或全局)中读取专用进程来实现并仅使用每个事件的全局或分区位置更新 Cassandra 中的事件。 (我对 MongoDB 有类似的事情 - 我有一个进程读取 'oplog' 并将 oplog 时间戳复制到事件中,因为 oplog 时间戳是完全有序的)。

另一种选择是从初始命令处理中删除 Cassandra 并改用 Kafka Streams:

  • 分区命令流通过与聚合的分区 KTable 连接来处理
  • 计算命令结果和事件
  • 以原子方式,KTable 使用更改的聚合进行更新,事件写入事件流,命令响应写入命令响应流。

然后您将拥有一个下游事件处理器,它将事件复制到 Cassandra 中以便于查询等(并且它可以将 Kafka 流位置添加到每个事件,因为它会给出类别排序)。如果您不想使用 Kafka 进行长期事件存储,这可以帮助赶上订阅等。 (要赶上进度,您只需尽可能多地阅读 Cassandra,然后从上次 Cassandra 事件的位置切换到 Kafka 的流式传输)。另一方面,Kafka 本身可以永久存储事件,所以这并不总是必要的。

我希望这有助于理解您可能遇到的权衡和问题。