【发布时间】:2018-09-13 13:42:31
【问题描述】:
我目前正在设计新系统,我坚信 CQRS+ES 非常适合。我想验证我的“大规模”设计预设是否听起来不错,并且我没有走错方向。
对我来说,由于一致性和网络边界一致,让每个聚合根(写入模型)都存在于自己的微服务中似乎是个好主意。
由于一致性保证,我认为可以安全地假设每个聚合实例可能只有自己的事件流。在实践中,为每个聚合 instance 结束一个事件存储将是一种矫枉过正,但在相同聚合类型的微服务之间共享一个或几个复制的事件存储对我来说似乎是明智的选择。如果不需要复制,我们甚至可以根据聚合 ID 对事件存储进行分片。
这样,由于很少有聚合类型范围的微服务,每个处理大量聚合实例的命令,扩展应该对系统的其余部分足够透明。
那么让投影仪(读取模型)也存在于它们自己的微服务中是有意义的,每个微服务都有自己的数据库,应该在相同类型的投影仪之间共享。
因为投影仪的查询界面对外部世界不是很友好(我假设投影仪提供类似 Repository 的界面来发出查询,在我看来,这在访问控制、速率限制等方面表现不佳),每个投影仪应该提供一些统一的网络接口,供 BFF (backend-for-frontend) 使用,实际上为一些 API 端点提供服务,确保安全,提供版本控制等。
tl;dr: 我提供了上面的图形表示(带圆圈),以用我糟糕的绘图来弥补我的糟糕措辞。 PS:重播服务是在新事件附加到事件存储时监视它们的服务,并将它们广播到感兴趣的订阅投影仪或流程管理器(未绘制),或者为具有陈旧或空数据库的投影仪重播整个事件序列。
这种 CQRS+微服务适配听起来不错,还是我从根本上误解了某些东西,整个设计都是垃圾?
UPD1:
为什么要在事件源和投影仪之间设置负载平衡器,如何平衡负载?
如果我生成多个相同类型的投影仪实例来处理来自繁重查询的额外负载,它们将如何监听事件?对我来说,只分配一个实例来完成所有事件处理工作、更新数据库等似乎很奇怪,因为随着负载的增加,它可能会过载。所以,分发事件处理也是有意义的,对吧?
另外,虽然我一直在写这篇文章,但我想将投影仪进一步拆分为“投影仪编写器”(监听事件并更新数据库中的共享状态)和“投影仪读取器”是否是个好主意"(那些侦听查询并返回状态的人),数据库作为事实来源和整合点。通过这种方式,我们可以更好地扩展非对称负载(小事件、大量查询)而无需任何成本。
必要条件之一是防止不同的投影仪编写器实例同时处理来自同一聚合的事件,因为使用无序事件更新表示会导致内部一致性的丧失和直接的灾难。
至于“如何”,我能想到几个解决方案:
-
为所有传入事件保留单个 RabbitMQ 队列,并让所有投影仪使用确认队列中的事件。 DB 更新后,投影仪编写器向 RabbitMQ 发出 ack,并且从队列中丢弃事件。否则,如果投影仪编写器由于某种原因死亡,事件将再次重新排队到下一个投影仪。
对于每个聚合,我们应该保留高度/修订号,并且只有在下一个修订号(包含在事件中)正好比当前修订号大一时才允许 UPDATE 成功。如果此条件不成立,则重新排队此事件,希望届时可以解决不一致问题,然后获取下一个。
最终,它会完成,并且给定足够多的聚合,它永远不需要重新排队。
-
放置某种调度器服务来监听事件,每个投影仪类型一个调度器。此调度程序应根据聚合 ID 的哈希将事件分发给投影仪编写器,因此,同一聚合始终由索引为
hash(event.aggregateId) % numberOfProjectorWriters的同一聚合处理。这将永远不会重新排队,但会提前终止 MQ,引入单点故障,并且如果由于某些节点死亡或动态缩放而导致投影仪写入器的数量发生变化,或者......
-
以某种方式使用标头交换来实现 #1 和 #2 的组合,以使消费者更喜欢同一组聚合 ID,但不会在中间消费者数量发生变化时搞砸。
【问题讨论】:
-
为什么/如何平衡投影仪(在右侧,“建筑”侧)?
-
@ConstantinGalbenu 我已经用答案更新了问题。欢迎反馈。此外,那些不是“建筑物”中的窗户,那些是圆柱体,例如。 DB象形图...
-
您正试图保持事件的顺序以尊重聚合仅;一般来说,读取模型(投影)需要total顺序的事件;否则,虽然有可能,但会导致非常复杂的 readmodel 更新程序
-
但是总的事件顺序一般不是不可能的吗?据我了解,由于没有“全局状态”,也没有单点一致性,怎么可能有同时独立发生的事件的总排序?当然,可以通过简单地遵循时间戳来提供某种顺序,希望最终保持一致,但这不是全部,对我来说,它与来自不同聚合的随机交错事件没有太大区别。我错了吗?
-
这不是不可能的。有使这成为可能的 EventStore 实现。例如,在 MongoDB 中,有时间戳使用客户端和服务器之间甚至分片之间的因果时钟保持同步
标签: architecture domain-driven-design cqrs event-sourcing