【问题标题】:Does Saga Pattern able to help to reverse Payouts incase if any Failure occurs?如果发生任何故障,Saga Pattern 是否能够帮助逆转支出?
【发布时间】:2020-04-26 07:19:54
【问题描述】:

我对 Saga Pattern 很陌生。我明白了,在 Saga 的帮助下,如果发生任何故障,我们都可以扭转局面。

无论我看到什么例子,它们大多类似于 订单服务 -> 支付服务 -> 其他服务,而在支付服务中,资金从客户到商户发生 >,并且如果如果其他服务发生任何故障,此付款交易可以逆转因为这里的资金从商家流向客户(在反向失败过程中)

但是,我的查询是:我有一个这样的反向场景:支付服务 -> 客户服务

支付服务中,资金从商户转移到客户

如果客户服务部出现任何故障,我们能否使用 Saga 撤销交易以进行支付? (即,将资金从客户转回给商家,以防万一发生任何故障)

以上可以使用 Saga 吗?希望我的查询很清楚。如果有人能在上面帮助我,我会很高兴。

【问题讨论】:

  • 寻找答案...

标签: design-patterns microservices cqrs event-sourcing saga


【解决方案1】:

saga 模式允许您协调多个操作,以防其中一个步骤失败,它允许您协调步骤以反转到目前为止所做的事情,因此您可以在无需实际交易即可完成所有步骤transactional

但是 sagas 不能改变你所做的每个步骤的性质。向客户收费是我们可以撤销的操作,因为我们可以创建补偿操作,例如将资金返还给客户。向客户付款是一项我们无法逆转的操作,因为一旦钱在客户手中,我们就无法收回。因此,saga 可以帮助您确保所有步骤都已发生或确保没有发生任何事情,但它不允许您逆转无法逆转的事情。

在这种情况下,你能做的就是把无法逆转的操作留到最后。例如,从客户钱包中打折(这是对您拥有的数据库的操作),然后进行实际支付。如果支付失败,saga 将允许您从客户的钱包中撤销减法,因此不会造成“损害”。


更新:根据评论添加的额外信息

Sagas(通常)通过消息传递来实现。传奇是编排器,它并没有真正执行实际步骤。相反,它发出命令,以便其他进程执行这些步骤并接收带有操作结果的消息或事件。

另外,请注意 saga 执行的步骤可能会出现暂时性故障或不可恢复/永久性故障。暂时失败可以重试,并且很可能会成功。永久失败不会通过重试成功。

考虑到这一切,我将解决问题如下:

  1. 发送命令以更新数据库并存储正在发生的事务。这会锁定资金,因此即使尚未付款,您也无法启动另一笔资金并最终支付太多钱。这里的故障可能是暂时的(数据库不可用)或永久性的(资金不足)。您可以重试暂时性故障,直到它正常工作并通知永久性故障。将带有操作结果的消息发送回 saga。
  2. 如果步骤 1 成功,则发送命令以执行支付。瞬时故障:外部服务不可用。最多重试 X 次。永久:支付数据无效。如果无法支付,saga 将补偿第 1 步并释放钱包中的资金。将消息发送回 saga 并返回结果:失败或来自外部 HTTP 服务的响应。
  3. 如果支付失败,发送命令回滚步骤 1。如果成功,发送命令确认支付并将响应存储在数据库中。请注意,在此处更新数据库时不应出现永久性故障。最多,您可能会出现暂时性故障(数据库不可用),但重试应该能够最终更新数据库。如果即使多次重试,操作也无法成功。您需要手动干预并解决问题,因为您无法回滚第 2 步。您应该为您的流程建模,以便此步骤可靠(验证和其他可能导致永久性故障的事情应在第 1 步或第 2 步中捕获) .

我希望这是有道理的。您需要确保涵盖所有场景,并且即使操作失败,您也可以可靠地发送消息。

【讨论】:

  • 嗨,首先,非常感谢您的回复.. 好的,我想问您一件我面临实际问题的事情:我正在做两件事:数据库更新和 REST 调用(即支付)。正如您所提到的,我可以先进行数据库更新,然后进行 REST 调用,但我不能这样做,因为数据库更新取决于 REST 调用(支付)响应。那么,在这种情况下,您对我有什么建议?
  • 谢谢 Francesc .. 我已经阅读了您的更新答案,我非常理解它.. 我忘了再提一点.. 目前,我的应用程序是 Monolithic。所以我想把它切换到微服务,这样我就可以使用 Sagas Pattern 解决这个支付问题。但是在我阅读了你的答案之后,我明白了升级到微服务对我来说没有用,因为无论如何都无法解决支付问题......而且我正在考虑保持我的单体应用......我的很小应用程序,仅供参考......我想我不需要在我的 Monolithic 中使用 Sagas,我认为 Sagas 不能应用于 Monolithic。
  • 我所能尝试的就是确保数据库服务器始终处于启动状态,但我如何保证在我的代码中不会在第 3 步得到任何 RunTimeException?我猜,任何替代解决方案都会更好,以解决可靠性问题..任何想法请..?
  • 在单体应用中使用 sagas 是没有问题的,只要你可以运行一个从队列中消费消息的进程。数据库也没有必要在 100% 的时间内启动(无论如何也不可能)。如果出现暂时性故障,队列允许您可靠地重试。如果您在步骤 3 中出现异常,将重试该操作,如果问题是暂时的,则在重试一次或多次后最终会起作用。我建议你看看 NServiceBus 的 saga 示例,它们涵盖了所有这些场景
  • 当然,我肯定会研究 Saga 示例。但我认为仍然存在问题。在我的 Monolithic 应用程序中。对于第 3 步,在从消息总线(假设我使用 Kafka)消耗消息之后,在数据库更新失败之后,这仍然是一个问题,对吗?我的意思是,如果我在第 3 步更新数据库时遇到一些 RunTimeException 怎么办? (即,在消息从 Kafka Broker 被消费后).. 由于我的数据库更新失败,我的钱包将不会更新.. 针对这种情况的任何解决方案,我实际上只是在这一点上被卡住了..跨度>
猜你喜欢
  • 1970-01-01
  • 2011-07-05
  • 2021-12-13
  • 1970-01-01
  • 2015-10-31
  • 2014-01-14
  • 2014-02-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多