从多个节点订阅一个事件流

问题描述

我们正在评估使用事件作为构建报告的来源,并增加了许多不同的选项。

我们目前正在服务结构集群中运行我们的系统(打算在未来某个时候迁移到 kubernetes),这意味着认情况下,各种事件流的订阅者将存在于多个节点上。我们研究了各种事件流实现(kafka、sqlStreamStore 和 EventStoreDb),并遇到了一个常见问题,即多节点订阅者都将尝试处理新消息并并行构建共享投影,这意味着我们需要依赖于对先前处理过的消息表或主键约束的检查。

可能的解决方案是我们坚持使用单个节点订阅者,但随着事件开始堆积,我无法看到这种扩展,或者我们直接从事件流中即时建立预测。有没有人遇到过或找到了解决这个问题的方法

解决方法

要运行并行投影,我建议考虑分区/分片策略。最明显的方法是确保单个投影类型将由特定订阅者处理(这可以通过例如侦听此投影类型或每个流类型处理的事件来完成)。有了这个,您就可以分配负载,而不会以竞争消费者的问题告终。

如果您的消费者竞争共享资源 - 例如您正在写入的读取模型很难保证处理顺序。理论上,您可以将流版本写入投影,然后验证它是否高于处理事件的版本。 但是,您可能会遇到以下情况:

  • 第一个处理程序 - 获取事件 1 和 2
  • 第二个处理程序 - 获取事件 3

如果第一个处理程序滞后,则第二个处理程序将写入投影,将版本设置为 3。如果根据版本执行检查,则事件 1 和 2 将被忽略。

此外,对于竞争的消费者,事件会乱序(例如,由于重试),然后您可能最终会出现版本号差距,并且无法验证是否可以轻松编写。

这对于来自单个流的投影来说很难。对于来自多个流的投影,它变得更加棘手。您最终可能需要为同一读取模型中的其他流维护不同的版本。从长远来看,这是无法管理的。

如果您的事件是典型的传输事件,这可以工作 - 所以“更新插入”。但是,如果您有商业活动,那么失去活动可能很关键。

另一种情况是当您不关心排序并且可以在可能错误的状态下使用数据一段时间(例如,首先应用“更新事件”中可能的内容,然后填充“创建”中的数据)事件”)。但是,这通常需要在投影业务逻辑上付出额外的努力,并确保充分处理无序情况。

我建议从一个作家开始。如果您发现性能问题,请定义您的分区/分片策略并确保您没有针对同一目标读取模型的竞争订阅者。使您的预测具有幂等性也很重要(因此,如果您两次获得相同的事件,它将产生单一效果)。