在 Kafka 之上并发写入事件源

Concurrent writes for event sourcing on top of Kafka

我一直在考虑使用 Apache Kafka 作为事件源配置中的事件存储。发布的事件将与特定资源相关联,传送到与资源类型相关联的主题,并按资源 ID 分片到分区中。因此,例如,创建类型为 Folder 且 ID 为 1 的资源将产生一个 FolderCreate 事件,该事件将传送到分区中的 "folders" 主题,该分区通过将 ID 1 跨主题中的分区总数进行分片。即使我不知道如何处理使日志不一致的并发事件。

最简单的情况是有两个可以相互无效的并发操作,例如一个更新文件夹,一个销毁同一个文件夹。在那种情况下,该主题的分区可能最终包含无效序列 [FolderDestroy,FolderUpdate]。这种情况通常通过对事件 as explained here 进行版本控制来解决,但 Kafka 不支持此类功能。

在这些情况下,如何确保 Kafka 日志本身的一致性?

我认为使用 Kafka 进行聚合事件溯源(在 DDD 意义上)或 'resources' 是可能的。一些注意事项:

  1. 序列化每个分区的写入,使用每个分区(或多个分区)的单个进程来管理它。确保您通过相同的 Kafka 连接连续发送消息,并在向命令发送者报告成功之前使用 ack=all,如果您无法承受回滚的话。确保生产者进程跟踪每个资源的当前成功事件 offset/version,因此它可以在发送消息之前自行进行乐观检查。
  2. 由于即使写入实际成功也可能返回写入失败,因此您需要重试写入并通过在每个事件中包含一个 ID 来处理重复数据删除,或者通过重新读取(最近的消息在) 流以查看写入是否实际工作。
  3. 以原子方式编写多个事件 - 只需发布一个包含事件列表的复合事件。
  4. 按资源 ID 查找。这可以通过在启动时读取分区中的所有事件(或特定跨资源快照中的所有事件)并将当前状态存储在 RAM 中或缓存在数据库中来实现。

https://issues.apache.org/jira/browse/KAFKA-2260 会以更简单的方式解决 1,但似乎停滞不前。

Kafka Streams 似乎为您提供了很多这样的功能。例如,4 是一个 KTable,您可以让您的事件生产者使用它来确定事件在发送之前是否对当前资源状态有效。