【问题标题】:How to rollback distributed transactions?如何回滚分布式事务?
【发布时间】:2019-07-11 16:53:04
【问题描述】:

我有三个不同的 Spring boot 项目,它们具有独立的数据库,例如 account-rest、payment-rest、gateway-rest。

  1. account-rest : 创建一个新帐户
  2. payment-rest : 创建一个新的付款
  3. gateway-rest : 调用其他端点

在 gateway-rest 有一个端点调用其他两个端点。

@GetMapping("/gateway-api")    
@org.springframework.transaction.annotation.Transactional(rollbackFor = RuntimeException.class)
public String getApi()
{
    String accountId = restTemplate.getForObject("http://localhost:8686/account", String.class); 
    restTemplate.getForObject("http://localhost:8585/payment?accid="+accountId, String.class);
    throw new RuntimeException("rollback everything");      
}

当我在网关或任何其他端点抛出异常时,我想回滚事务并还原所有内容。

我该怎么做?

【问题讨论】:

    标签: spring spring-boot microservices


    【解决方案1】:

    为此,您需要外部交易管理系统。当它在所有服务上完成时,它将处理分布式事务和提交/回滚。

    可能的流程示例:

    • 请求来了
    • gateway-rest 启动分布式事务和本地事务,并向 payment-rest 发送请求(带有事务 id)。具有事务的线程一直存在,直到所有本地事务完成为止。
    • payment-rest 知道全局事务并开始自己的本地事务。
    • 当所有本地事务都标记为已提交时,TM(事务管理器)向每个服务发送请求以关闭本地事务并关闭全局事务。

    【讨论】:

    • 好主意,但我更多的是寻找实现而不仅仅是理论。
    • 这种方法是 2PC,在微服务中是非常不鼓励的方法,实际上是反模式。如上所述,Saga 是可行的方法。
    【解决方案2】:

    不可能通过 rest 或类似的方式回滚外部依赖项。 你唯一能做的就是补偿错误,你可以使用像 SAGA 这样的模式

    希望对你有帮助

    【讨论】:

      【解决方案3】:

      你基本上是在做双重持久性。理想情况下,这不是一件好事,原因有两个

      1. 它会增加延迟,从而直接影响用户体验
      2. 如果其中一个失败了怎么办?

      正如另一个答案指出的那样,SAGA 模式是发布补偿交易的一种选择。

      另一种选择,最好是通过同步写入一个服务,然后使用更改数据捕获 (CDC) 异步更新另一个服务来避免双重持久性。如果我们能以这种方式进行设计,我们可以确保原子性(全有或全无),因此回滚场景本身可能不会出现。

      如果有帮助,也请参考这两个答案: https://stackoverflow.com/a/54676222/1235935 https://stackoverflow.com/a/54527066/1235935

      一定要避免分布式事务或两阶段提交。当事务协调器在准备阶段和提交阶段之前失败时,这不是一个好的解决方案,并且会产生大量的操作开销、锁定等。当事务协调器的数据损坏时,会发生更糟糕的事情。

      【讨论】:

        【解决方案4】:

        在您的情况下,您可以使用许多其他人提到的 saga,但它们本质上需要事件和异步。

        如果您想要一种同步类型的 API。你可以做类似的事情:

        首先让我们以亚马逊为例,创建订单并从钱包中取出余额并完成订单:

        create Order in PendingState
        reserveBalance in Account service for order id
        
        if balance reserved change Order state to Confirmed (also having the transaction id for the reserve) and update reserveBalanceConsumed to Account Service
        
        else change Order state to Cancelled with reason , "not enough Balance"
        

        现在有些情况下,我们说帐户服务余额已保留,但由于某种原因订单未确认。

        然后有人可以定期检查是否有某个订单和时间的保留余额>30 分钟,然后检查该订单是否被标记为使用该交易 id 确认,调用 reserveBalanceConsumed ,否则取消该订单,原因是“一些错误请重试”,将余额标记为免费

        现在这些类型的系统构建起来很复杂。通常使用 Saga 模式以获得更简单的结构。

        【讨论】:

          猜你喜欢
          • 2011-04-26
          • 2020-07-07
          • 1970-01-01
          • 2011-06-22
          • 1970-01-01
          • 1970-01-01
          • 2010-10-04
          • 2021-10-17
          相关资源
          最近更新 更多