首页 > 解决方案 > 丢失的事件如何重播?

问题描述

我正在尝试了解有关 CQRS 和事件溯源(事件存储)的更多信息。

我的理解是,在这种情况下通常不使用消息队列/总线 - 消息总线可用于促进微服务之间的通信,但它通常不专门用于 CQRS。但是,我目前看到的方式 - 消息总线将非常有用,可以确保读取模型最终同步,从而最终保持一致性,例如,当托管读取模型数据库的服务器重新联机时。

我了解 CQRS 通常可以接受最终的一致性。我的问题是;读取端如何知道它与写入端不同步?例如,假设每天在事件存储中创建 2,000,000 个事件,并且还有 1,999,050 个事件被写入读取存储。剩下的 950 个事件没有写入,因为某处的软件错误或因为托管读取模型的服务器离线几秒钟等。最终一致性如何在这里工作?应用程序如何知道重播一天结束时丢失的 950 个事件或由于十分钟前的停机时间而丢失的 x 个事件?

在过去一周左右的时间里,我在这里阅读了有关从事件存储重放消息的问题,例如:CQRS - Event replay for read side,但是没有人谈论这是如何完成的。我是否需要设置一个每天运行一次的计划任务,并重播自计划任务上次成功之日起创建的所有事件?有没有更优雅的方法?

标签: domain-driven-designcqrsevent-sourcing

解决方案


根据要求,我在项目中使用了两种方法:

  1. 同步的进程内读取模型。在事件被持久化后,在相同的请求生命周期中,在相同的进程中,Readmodels 会收到这些事件。如果 Readmodel 发生故障(错误或可捕获的错误/异常),则会记录错误,并且 Readmodel 会被跳过,下一个 Readmodel 会收到事件等。然后遵循 Sagas,这可能会生成生成更多事件的命令并重复循环。

当业务可以接受 Readmodel 故障的影响时,当 Readmodel 数据的准备情况比故障风险更重要时,我会使用这种方法。例如,他们希望数据在 UI 中立即可用。

错误日志应该可以在某些管理面板上轻松访问,以便有人会查看它,以防客户端报告写入/命令和读取/查询之间的不一致。

如果您的 Readmodel 相互耦合,这也适用,即一个 Readmodel 需要来自另一个规范 Readmodel 的数据。虽然这看起来很糟糕,但事实并非如此,它总是取决于。在某些情况下,您可以用弹性来交换更新程序代码/逻辑重复。

  1. 异步的、在另一个进程中的readmodel 更新程序。当我将 Readmodel 与其他 Readmodel 完全分离时使用此方法,此时 Readmodel 的故障不会导致整个读取面朝下;或者当 Readmodel 需要另一种语言时,不同于单体。基本上这是一个微服务。当在 Readmodel 中发生不好的事情时,有必要通知一些权威的更高级别的组件,即通过电子邮件或 SMS 或其他方式通知管理员。

Readmodel 还应该有一个状态面板,其中包含有关它已处理的事件、是否存在间隙、是否存在错误或警告的各种指标;它还应该有一个命令面板,管理员可以在其中随时重建它,最好不要系统停机。

在任何方法中,Readmodels 都应该很容易重建。

您将如何在拉式方法和推式方法之间进行选择?您会使用带有推送(事件)的消息队列吗

我更喜欢基于拉的方法,因为:

  • 它不使用另一个有状态的组件,如消息队列,另一个必须管理的东西,它消耗资源并且可能(所以它会)失败
  • 每个 Readmodel 都以它想要的速率消耗事件
  • 每个 Readmodel 都可以随时轻松更改它使用的事件类型
  • 通过从一开始就请求所有事件,可以随时轻松地重建每个 Readmodel
  • 事件的顺序与真相的来源完全相同,因为你从真相的来源中拉出来

在某些情况下,我会选择消息队列:

  • 即使事件存储不可用,您也需要事件可用
  • 你需要有竞争力的/平行的消费者
  • 您不想跟踪您使用的消息;当它们被消耗时,它们会自动从队列中删除

推荐阅读