如何在微服务之间回滚事务?

7
我们采用微服务架构,大多数情况下每个微服务都是独立的。但由于一些历史原因,我们必须从一个微服务中调用另一个微服务。
例如:以下方法是“Legal Service”的一部分。
@Autowired
public ServiceManager UserServiceManager;

public void updateUserLegalData(){

    //do db update of Legal info for the user

   userServiveManager.setAcceptedLegal(true);


}

以上有两个数据库事务,一个正在更新legalService数据库,另一个正在更新UserService数据库。请注意,userService是在单独的虚拟机上运行的微服务

我们发现情况是legal Service数据库已更新,但对userService的调用失败(内部服务器错误)。这将导致应用程序处于不一致状态。我们该如何以推荐的方式解决这个问题?

谢谢


1
如果只有两个服务,那么一种方法是从第一个服务的事务方法中调用第二个服务。首先更新本地数据库,然后调用第二个服务。如果第二次调用失败,则抛出异常。这将导致第一个服务也回滚。如果第二个服务成功,您可以放心使用数据库状态。 - Vikash
4个回答

3

这种情况只能通过JTA全局/分布式事务来处理。JTA是Java EE标准的一部分,可以有不同的实现者。Atomikos经常被选为工具。

这里有Dave Syer(Spring生态系统贡献者)的优秀文章,它还包含工作示例。它有点过时,但仍然相关。您可以在他的示例之上应用一些更现代的Spring抽象。

我为我的书创建了一些GitHub中JTA事务的示例。请注意,已模拟出错误,并且事务跨越了JMS和JDBC数据源。

但请记住,跨多个数据源的JTA事务很慢,因为涉及到两阶段提交算法。因此,人们经常尝试避免它们,而是以某种实用的方式处理不一致性。


http://www.apress.com/9781484207949。但是它还没有完成。目前正在进行最后一章的工作。 - luboskrnac
“JTA事务较慢,现在通常被认为是不良架构模式”- 我认为相反是正确的。如今,JTA事务已经被优化到了这样的程度,几乎没有任何额外开销。当您只使用单个事务资源或一个事务性资源和一个非事务性资源时,还可以应用LRCO,从而降低与两阶段提交相关的任何可能成本。” - Arjan Tijms

3

不要进行分布式事务。

为了与您现有的遗留系统进行集成,一种方法可能是创建一个独立的(微)服务,该服务监听来自userService的更新事件,并将相应的更新转发给legalService。Spring Integration可能适合这样的任务。

祝好, Michael


2
如果您在互联网上阅读了一些相关主题的内容,您会发现分布式事务是一个重大的争议点。但有一个答案是所有人都同意的,那就是分布式事务不是解决此问题的方法。它们太笨拙和容易出错,我们无法依靠它们来保持数据一致性。
那么我们的选择是什么呢?目前人们正在尝试通过Apache Kafka或事件源(集中保存改变数据的事件而不是保存数据本身)来协调微服务事务。那么这些方案有什么问题呢?它们与我们通常习惯的编程模型非常不同,在技术和组织层面上也相当复杂,因此你开始为了解决技术挑战而编写代码,而不是为了解决业务问题而编写代码。
那么有什么替代方案呢?我个人开发了另一个概念,并写了一篇博客介绍它,也许对您很有趣。基本上,它使用完整的微服务设计原则和Spring Boot + Netflix在J2EE容器中,并充分利用事务处理。这里无法详细说明所有细节,如果您感兴趣,可以从下面的链接中阅读。

使用Spring Boot和Netflix构建微服务和事务


1
跨微服务的事务可能会变得复杂,并且可能会减慢系统速度,解决分布式事务问题的最佳方法之一是完全避免它们。如果您避免在微服务之间实现分布式事务,则不会陷入这种情况。
如果您必须在微服务之间实现分布式事务,则我认为有几种方法:
两阶段提交协议 最终一致性
在您的情况下,我建议使用消息总线和标志在服务之间进行通信。因此,如果合法服务将数据添加到合法数据库中,请对该记录进行锁定并在消息总线上发送消息。当用户服务运行时,它将接收信息并在其端更新数据库,并将确认消息发送到消息总线上。一旦收到确认消息,则删除锁定,否则在一定时间后删除/回滚记录。虽然看起来很复杂,但在您的情况下,这是可靠且无故障的解决方案。

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