domain-driven-design - 丢失的事件如何重播?
问题描述
我正在尝试了解有关 CQRS 和事件溯源(事件存储)的更多信息。
我的理解是,在这种情况下通常不使用消息队列/总线 - 消息总线可用于促进微服务之间的通信,但它通常不专门用于 CQRS。但是,我目前看到的方式 - 消息总线将非常有用,可以确保读取模型最终同步,从而最终保持一致性,例如,当托管读取模型数据库的服务器重新联机时。
我了解 CQRS 通常可以接受最终的一致性。我的问题是;读取端如何知道它与写入端不同步?例如,假设每天在事件存储中创建 2,000,000 个事件,并且还有 1,999,050 个事件被写入读取存储。剩下的 950 个事件没有写入,因为某处的软件错误或因为托管读取模型的服务器离线几秒钟等。最终一致性如何在这里工作?应用程序如何知道重播一天结束时丢失的 950 个事件或由于十分钟前的停机时间而丢失的 x 个事件?
在过去一周左右的时间里,我在这里阅读了有关从事件存储重放消息的问题,例如:CQRS - Event replay for read side,但是没有人谈论这是如何完成的。我是否需要设置一个每天运行一次的计划任务,并重播自计划任务上次成功之日起创建的所有事件?有没有更优雅的方法?
解决方案
根据要求,我在项目中使用了两种方法:
- 同步的进程内读取模型。在事件被持久化后,在相同的请求生命周期中,在相同的进程中,Readmodels 会收到这些事件。如果 Readmodel 发生故障(错误或可捕获的错误/异常),则会记录错误,并且 Readmodel 会被跳过,下一个 Readmodel 会收到事件等。然后遵循 Sagas,这可能会生成生成更多事件的命令并重复循环。
当业务可以接受 Readmodel 故障的影响时,当 Readmodel 数据的准备情况比故障风险更重要时,我会使用这种方法。例如,他们希望数据在 UI 中立即可用。
错误日志应该可以在某些管理面板上轻松访问,以便有人会查看它,以防客户端报告写入/命令和读取/查询之间的不一致。
如果您的 Readmodel 相互耦合,这也适用,即一个 Readmodel 需要来自另一个规范 Readmodel 的数据。虽然这看起来很糟糕,但事实并非如此,它总是取决于。在某些情况下,您可以用弹性来交换更新程序代码/逻辑重复。
- 异步的、在另一个进程中的readmodel 更新程序。当我将 Readmodel 与其他 Readmodel 完全分离时使用此方法,此时 Readmodel 的故障不会导致整个读取面朝下;或者当 Readmodel 需要另一种语言时,不同于单体。基本上这是一个微服务。当在 Readmodel 中发生不好的事情时,有必要通知一些权威的更高级别的组件,即通过电子邮件或 SMS 或其他方式通知管理员。
Readmodel 还应该有一个状态面板,其中包含有关它已处理的事件、是否存在间隙、是否存在错误或警告的各种指标;它还应该有一个命令面板,管理员可以在其中随时重建它,最好不要系统停机。
在任何方法中,Readmodels 都应该很容易重建。
您将如何在拉式方法和推式方法之间进行选择?您会使用带有推送(事件)的消息队列吗
我更喜欢基于拉的方法,因为:
- 它不使用另一个有状态的组件,如消息队列,另一个必须管理的东西,它消耗资源并且可能(所以它会)失败
- 每个 Readmodel 都以它想要的速率消耗事件
- 每个 Readmodel 都可以随时轻松更改它使用的事件类型
- 通过从一开始就请求所有事件,可以随时轻松地重建每个 Readmodel
- 事件的顺序与真相的来源完全相同,因为你从真相的来源中拉出来
在某些情况下,我会选择消息队列:
- 即使事件存储不可用,您也需要事件可用
- 你需要有竞争力的/平行的消费者
- 您不想跟踪您使用的消息;当它们被消耗时,它们会自动从队列中删除
推荐阅读
- c# - Umbraco 8:SetValue 有一些无效参数 - 将新图像保存到媒体 C#
- php - 在 php nlp 相似性中从用户那里获取输入
- python - 如何将 for 循环结果合并到一个列表中
- c - 从c中的另一个文件调用函数
- sql-server - DATALENGTH() 适用于文字,但不适用于变量
- java - 为什么我的 Firebase addvalueEventlistener 不会被调用?
- javascript - 异步函数不等待等待结束
- apache-kafka - Flink:为 FlinkKafkaConsumer 分配水印
- c++ - 我需要帮助在一个类中输入两个字符串并获取它的输出
- microsoft-graph-api - 查询 149 天以上时,MS Graph API CalendarView 是否存在问题?