REST和事务回滚

16

我有几个不同的RESTful服务,它们托管在不同的服务器上,使用不同的数据库。我有一些RESTful服务会在事务单元中调用多个这样的服务。如果其中任何一个RESTful服务失败,我们就会出现数据一致性问题。有没有一种简洁的架构方式来协调回滚操作?或者说采用事务管理器的方式是否可行?

举个简单的例子,RESTful服务1进行POST请求并将thingamajig的物品数量减少1。RESTful服务2请求付款。如果服务2失败了,那么我们该如何干净地在服务1上实现回滚操作,而无需创建新的RESTful退款服务(如果必要,创建此服务也是可以接受的)? 我在寻找符合REST原则的上述问题的架构答案。


你不能使用DDD和最终一致性吗?这样,您就不需要在失败时回滚,只需重试。我认为这比在REST服务之上实现多阶段提交更容易。 - inf3rno
类似问题:https://dev59.com/z1DTa4cB1Zd3GeqPMtlb#3837376 - inf3rno
3个回答

15
您的答案:https://dev59.com/tXM_5IYBdhLWcg3waSX9#1390393 由于REST中客户端维护客户端状态,服务器维护资源状态,因此您不能使用事务。因此,如果您希望客户端维护资源状态,则不符合REST要求,因为这将违反无状态约束。违反无状态约束通常会导致可扩展性差。在这种情况下,它将导致水平可扩展性差,因为您必须在实例之间同步正在进行的事务。因此,请不要尝试在REST服务之上构建多阶段提交。
可能的解决方案:
  • 您可以坚持立即一致性,仅使用一个网络服务而不是两个。对于像数据库、文件系统等资源来说,多阶段提交是必须的。当您将较大的REST服务分解并将这些资源的使用移动到多个较小的REST服务中时,如果分解不正确,则可能会出现问题。这是因为其中一个REST服务将需要一个它无权访问的资源,因此它必须使用另一个REST服务来访问该资源。这将迫使多阶段提交代码移动到更高的抽象级别,即REST服务的级别。您可以通过合并这两个REST服务并将代码移动到其所属的较低抽象级别来解决此问题。
  • 另一种解决方法是使用具有最终一致性的REST,以便您可以立即响应202 accepted,并稍后处理已接受的请求。如果选择此解决方案,则在开发您的应用程序时必须意识到REST服务不始终同步。当然,此方法仅适用于内部REST服务,您可以确定客户端是否重试REST服务,因此如果编写和运行客户端代码。
  • 另一种,可能是最丑陋的解决方法是将每个事务都存储为资源,以便您可以发布提交和回滚。我认为这种可能的解决方案不可行,因为它将违反统一接口约束。我们将使用POST /transactions/ {resource: "/forums/12/messages/45", method: "PUT", data: "..."}POST /transactions/1/commit代替例如PUT /forums/12/messages/45

7年后的一些补充:如果我们需要回滚而不是恢复所有内容,我会使用事件溯源和补偿机制。 REST服务可以基于存储的事件进行调用,并在放弃之前重试无数次,直到我们需要补偿(通常手动处理,如果它很少并且难以自动化)。拥有退款服务是补偿机制的一个很好的例子,所以我认为这是正确的方法。至于最后一点,当需要多个步骤来解决某些问题时,例如付款、交付等,可以使用类似事务的资源。 - inf3rno

6
分布式事务很复杂,需要每个参与系统支持回滚的概念。在您的服务中,它们都必须支持某种形式的回滚。在分布式系统中协调这样的事情可能不切实际或不可取的同步方式。在这种情况下,您需要异步回滚,系统最终会在将来的某个时间点达到一致性。显然还有许多其他细节(超时、错误处理、重试等)。
有关最终一致性的更多详细信息,请查看维基百科条目 here

尽管其他评论者已经提出了类似的想法,将两个不同的服务合并成一个(可能属于两个团队,为不同的垂直领域/客户提供服务)是不可行的。由于这个答案与在多团队企业环境中解决这个问题更相关,所以我接受了这个答案。 - cvam

0

基本问题在于你需要在一个默认不支持事务的环境(HTTP)中使用事务 - 在数据库意义上(因为在HTTP中,事务是成功的请求 - 响应周期)。

@leeor回答的内容完全正确,我想补充的是我如何从设计方面解决这个问题。

所以你需要一个单一的端点,可能是/transactions。通过POST方法添加一个新的事务(带有所有必要的细节),该事务是不可变的 - 创建后,只能通过GET方法请求其数据/状态。可以更新事务状态/数据的是服务器本身。

在幕后(在事务创建期间),应为参与事务的每个资源创建一个快照(稍后可以撤消)。然后开始执行所有操作,并在任何失败的情况下撤消所有快照。你没有提到任何技术,所以很难建议合理的东西。你肯定需要全面的日志记录。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接