基于 Kafka 的事件溯源并发写入

Posted

技术标签:

【中文标题】基于 Kafka 的事件溯源并发写入【英文标题】:Concurrent writes for event sourcing on top of Kafka 【发布时间】:2017-11-05 14:55:47 【问题描述】:

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

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

在这些情况下,可以做些什么来确保 Kafka 日志本身的一致性?

【问题讨论】:

你读过这个吗? ***.com/questions/17708489/… 【参考方案1】:

我认为可能将 Kafka 用于聚合(在 DDD 意义上)或“资源”的事件源。一些注意事项:

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

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

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

【讨论】:

如果每个分区只有一个进程,则无需等待确认-所有处理都可以在该进程内序列化,并且某些消息仍然存在并不重要就顺序/一致性而言,电线(保证写入是单独的问题,可能不应同时混入解释中) 但是如果资源1,事件写入A失败,然后你写入事件B而不等待A的确认,并且B写入正常呢?所以 B 在没有 A 的流中,打破了不变量。或者 A 以某种方式延迟并在 B 之后结束在流中。或者这是不可能的,因为两者都将使用相同的 TCP 连接?无论如何,如果是这样的话,这会简化一些事情。 我认为不可能将事件 A 然后 B 从单个程序发送到单个 kafka 通道并最终只有 B 在那里。它要么什么都没有,要么只有 A,要么是 A 和 B。生产者连接是具有重新连接的无损 TCP 流。当然,如果您故意弄乱您的 kafka 代理(将消息删除时间设置为几秒钟,在处理清理内容等过程中重新启动它)一切皆有可能,但在正常操作中(即使有错误),事件不应该从生产者流的中间迷路。 恐怕这是可能的,我们已经看到了这种情况。 Producer 发送 A,broker 拒绝它,因为重新平衡刚刚发生,它不再是该分区的领导者。生产者发现新的领导者并发送 B. B 被存储但 A 没有。仅当您更喜欢吞吐量而不是一致性并且愿意为(相对)正常操作时的消息丢失付出代价时,才使用 acks=0。 对于#2,我强烈建议不要尝试读取流的内容,以确定在再次发布之前是否已发布记录,以避免重复。这使生产者的实现显着复杂化。 Kafka 背后的总体设计理念是至少一次交付,这意味着消费者生态系统中的幂等行为。您的消费者应该已经能够处理重复事件。

以上是关于基于 Kafka 的事件溯源并发写入的主要内容,如果未能解决你的问题,请参考以下文章

基于Kafka构建事件溯源模式的微服务

Spring Kafka - 事件溯源 - 如何使用 Kafka + KafkaStreams API 查询某些实体状态的示例

Kafka如何实现每秒上百万的超高并发写入?

互融云区块链溯源防伪系统开发,超高并发,全程追溯

面试官:消息中间件如何实现每秒几十万的高并发写入?石杉的架构笔记

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