如何回滚分布式事务?

时间:2019-02-18 10:22:03

标签: spring spring-boot microservices

我有三个不同的Spring Boot项目,它们具有分开的数据库,例如帐户剩余,付款剩余,网关剩余。

  1. 帐户休息:创建一个新帐户
  2. 剩余付款:创建新的付款
  3. 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");      
}

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

我该怎么做?

4 个答案:

答案 0 :(得分:2)

不可能通过rest或类似的东西回滚外部依赖关系。 唯一可以做的就是补偿误差,可以使用SAGA之类的模式

我希望能为您提供帮助

答案 1 :(得分:1)

基本上,您正在执行双重持久性。由于两个原因,这在理想情况下不是一件好事

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

另一个答案指出,SAGA模式是过帐补偿交易的一种选择。

另一种选择,最好是通过同步写入一个服务,然后使用Change Data Capture(CDC)异步更新另一个服务来避免双重持久性。如果以这种方式进行设计,则可以确保原子性(全部或全部),因此回滚方案本身可能不会浮出水面。

如果有帮助,请同时参考以下两个答案: https://stackoverflow.com/a/54676222/1235935 https://stackoverflow.com/a/54527066/1235935

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

答案 2 :(得分:0)

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

可能的流量示例:

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

答案 3 :(得分:0)

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

如果您想要同步的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分钟,然后说再检查该订单是否被标记为已确认,并调用reserveBalanceConsumed,否则取消该订单,原因是“某些错误请重试”,将余额标记为免费

现在,这些类型的系统都是复杂的。通常使用Saga模式来简化结构。