我们正在使用Lagom来开发我们的微服务集。这里的诀窍是虽然我们使用事件源并将事件持久化到cassandra但我们必须将数据存储在一个图形数据库中,因为它将是为大多数查询服务的因为用例
根据Lagom的文档,在命令处理程序将事件持久化到cassandra之后,所有插入Graph数据库(或任何其他数据库)的行必须在ReadSideProcecssor
中完成,其次是CQRS的哲学。
现在我们面临的问题就在这里。我们认为ReadSideProcecssor
是一个在生成并保持事件后触发的侦听器。我们想要的是我们可以将响应从ReadSideProcecssor
返回到ServiceImpl
。将用户添加到系统时的示例,图表生成的唯一ID必须作为响应标头之一返回。如何在Lagom中实现这一点,因为响应是从setCommandHandler
而不是ReadSideProcessor
构建的。
此外,我们需要确保如果由于图形方面的任何错误,API应通知客户端请求已失败,但ReadSideProcessor
中发生的异常不会传播到PersistentEntity
}或ServiceImpl
类。怎么能实现呢?
非常感谢任何帮助。
答案 0 :(得分:3)
读取端处理器不是附加到命令的侦听器 - 它实际上与持久性实体完全断开连接,它可能在不同的节点上运行,在不同的时间运行,甚至可能在未来几年内运行添加一个新的读取侧处理器,它首先加快了历史上所有旧事件的速度。如果读取侧处理器与命令同步连接,那么它将不是CQRS,命令和查询端之间不会有隔离。
读取侧处理器实质上是在数据库中轮询新事件,并在检测到它们时对其进行处理。您可以随时添加新的读取处理器,它将获取所有历史记录中的所有事件,而不仅仅是添加的新事件,这是有关事件采购的优点之一,您无需预测从一开始您的所有查询需求,您可以根据查询需要添加它们。
进一步解释为什么你不想在两者之间建立连接 - 如果事件持续成功会发生什么,但是图表数据库上的更新失败了?也许图表db崩溃了。命令是否必须重试?该事件是否必须删除?如果执行更新的节点在有机会解决问题之前崩溃会发生什么?现在,您的阅读方与您的实体处于不一致状态。连接它们会导致许多故障情况不一致 - 例如,当您向公用事业公司更新地址时,但您的账单仍然转到旧地址,并且您联系他们,他们说“是的,您的新地址是在我们的系统中进行了更新“,但它们仍然会转到旧地址 - 如果您尝试将读取端和写入端连接在一起,那么您正在为用户签署这种糟糕的用户体验。断开连接允许Lagom确保您在写入端发出的事件与读取端的消耗之间的一致性。
因此,为了解决您的特定问题:ID生成应该在写入端完成,或者,如果在读取端生成后续ID,它还应该提供一种将写入侧的ID映射到读取的方法身份证。至于读取端的处理错误 - 所有验证都应该在写入端完成 - 写入端应该确保它永远不会发出无效的事件。
现在,如果读取端处理器遇到无效的东西,那么它有两个选项。一种选择是它可能会失败。在许多情况下,这是一个很好的选择,因为如果某些内容无效或不一致,那么很可能是您有错误或某种形式的损坏。你不想做的是继续处理好像一切都很开心,因为这可能会使数据损坏或不一致更糟。相反,读取端处理器停止,您的监视应该检测到错误,并且您可以进入并找出错误是什么或修复损坏。当然,这样做有一些缺点,你的阅读方将开始落后于写入方,而它无法处理新事件。但这也是CQRS的一个优点 - 写入端能够继续工作,继续强制执行一致性等,故障只是与读取端隔离,并且仅在更新读取端时。由于这个错误而不是整个系统停止运行而拒绝接受新的请求,所以它与问题所在的位置隔离开来。
读取方具有的另一个选项是它可以在某处存储错误 - 例如,将事件存储在死信表中,或者引发某种故障单,然后继续处理。这样,您可以在事后修复事件。这确保了更高的可用性,但是如果它未能处理的事件对后续事件的处理很重要,那么你可能会陷入更大的混乱。
现在这确实会对您可以做什么和不可以做什么做出具体限制,但是我无法真正预测那些没有具体了解您的用例的人知道如何解决它们。常见约束是设置验证 - 例如,您如何确保电子邮件地址对系统中的单个用户是唯一的? Greg Young(CQRS人)写了这篇关于这些类型问题的博客文章:
http://codebetter.com/gregyoung/2010/08/12/eventual-consistency-and-set-validation/