从多个节点订阅事件流

Subscribing to an event stream from multiple nodes

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

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

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

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

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

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

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

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

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

如果您的事件是典型的交通事件,这可能会奏效 - 所以“upserts”。但是,如果您有业务活动,那么失去活动可能很关键。

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

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