Spring Boot 应用中的事务管理与 Feign 调用问题分析及解决

Spring Boot 应用中的事务管理与 Feign 调用问题分析及解决

  • [Spring Boot 应用中的事务管理与 Feign 调用问题分析及解决](#Spring Boot 应用中的事务管理与 Feign 调用问题分析及解决)
    • 问题背景
    • 问题分析
    • 解决方案
      • [方案一:提交事务后再调用 Feign](#方案一:提交事务后再调用 Feign)
      • [方案二:使用 `PROPAGATION_REQUIRES_NEW`](#方案二:使用 PROPAGATION_REQUIRES_NEW)
      • [方案三:使用 `@Async` 异步调用](#方案三:使用 @Async 异步调用)
    • 总结

Spring Boot 应用中的事务管理与 Feign 调用问题分析及解决

在微服务架构下,遇到这样的场景:一个服务需要调用另一个服务的接口来进行一系列操作,而这些操作又需要保证事务的一致性。然而,在实际开发过程中,可能会遇到一些问题,比如事务中所做的更改无法被 Feign 调用所看到。本文将以开发中遇到的一个问题当做一个例子来探讨这一问题及其解决方案。

问题背景

有一个内部商家管理模块,其主要职责是添加新的商家。在这个过程中,需要做如下几件事(已简化):

  1. 创建商家信息,并分配相应的角色。
  2. 为商家分配会员。

在这个过程中,为了保证数据的一致性和完整性,需要在方法上使用 @Transactional 注解。然而,在添加完商家之后,尝试调用 purchaseAPI.addMember 方法为商家分配会员时,发现无法获取到刚刚添加的 sys_supplier 商家信息。

问题分析

这个问题的根本原因在于事务隔离级别和 Feign 调用之间的交互。当在一个事务中执行了一系列操作后,这些操作所产生的数据更改只有在事务提交之后才会对外可见。而在同一事务中,Feign 调用实际上是在一个新的上下文中执行的,这意味着它不会看到未提交的更改。

可能的原因

  1. 事务隔离级别 :默认情况下,Spring 的事务隔离级别是 ISOLATION_DEFAULT,这意味着它会使用底层数据库的默认隔离级别。如果数据库默认隔离级别是 READ_COMMITTED 或 REPEATABLE_READ,那么在事务中更新的数据在当前事务提交之前对其他事务不可见。

  2. Feign 调用与事务:Feign 调用实际上是在当前事务之外发生的,这意味着它不会受到当前事务的影响。因此,如果您在事务中更新了数据,Feign 调用将不会看到这些未提交的更改。

解决方案

方案一:提交事务后再调用 Feign

确保在调用 Feign 服务之前先提交事务。这可以通过将 Feign 调用放在事务范围之外来实现。例如:

java 复制代码
@Override
public void internalSupplierAdd(InternalSupplierDTO internalSupplierDTO, LoginUser loginUser) {
    // ... (之前的代码)

    //6.添加内部供应商
    SysSupplier sysSupplier = new SysSupplier();
    BeanUtils.copyProperties(internalSupplierDTO, sysSupplier);
    sysSupplierMapper.insert(sysSupplier);

    // 提交事务
    PlatformTransactionManager transactionManager = TransactionAspectSupport.currentTransactionManager();
    TransactionDefinition def = new DefaultTransactionDefinition();
    TransactionStatus status = transactionManager.getTransaction(def);
    transactionManager.commit(status);

    //7.为商家分配会员
    Result<MemberLevle> memberLevelResult = purchaseAPI.addMemberAndLevel(tenantId);
    if (!memberLevelResult.isSuccess()) {
        //接口请求失败
        throw new JeecgBootException("分配会员出错!");
    }

    //...(后面的代码)

}

方案二:使用 PROPAGATION_REQUIRES_NEW

如果希望在同一个方法中保持事务的原子性,但又需要确保 Feign 调用能够看到最新的数据,可以使用 PROPAGATION_REQUIRES_NEW 传播行为。这将创建一个新的事务,即使在当前事务中也是如此。

java 复制代码
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void internalSupplierAdd(InternalSupplierDTO internalSupplierDTO, LoginUser loginUser) {
    // ... (之前的代码)

    //6.添加商家
    SysSupplier sysSupplier = new SysSupplier();
    BeanUtils.copyProperties(internalSupplierDTO, sysSupplier);
    sysSupplierMapper.insert(sysSupplier);

    //7.为商家分配会员
    Result<MemberLevle> memberLevelResult = purchaseAPI.addMemberAndLevel(tenantId);
    if (!memberLevelResult.isSuccess()) {
        //接口请求失败
        throw new JeecgBootException("分配会员出错!");
    }

    //...(之后的代码)
}

方案三:使用 @Async 异步调用

如果分配会员的操作不是必须同步完成的,可以考虑使用异步调用来避免阻塞事务。

java 复制代码
@Override
@Transactional(rollbackFor = Exception.class)
public void internalSupplierAdd(InternalSupplierDTO internalSupplierDTO, LoginUser loginUser) {
    // ... (之前的代码)

    //6.添加商家
    SysSupplier sysSupplier = new SysSupplier();
    BeanUtils.copyProperties(internalSupplierDTO, sysSupplier);
    sysSupplierMapper.insert(sysSupplier);

    //7.为商家分配会员
    asyncPurchaseService.addMemberAndLevelAsync(tenantId);

    //... (之后的代码)
}

// 异步服务
@Service
public class AsyncPurchaseService {

    @Autowired
    private PurchaseAPI purchaseAPI;

    @Async
    public void addMemberAndLevelAsync(Integer tenantId) {
        Result<MemberLevle> memberLevelResult = purchaseAPI.addMemberAndLevel(tenantId);
        if (!memberLevelResult.isSuccess()) {
            //接口请求失败
            throw new JeecgBootException("分配会员出错!");
        }
    }
}

总结

通过以上分析和解决方案,可以有效地解决事务中 Feign 调用无法看到未提交更改的问题。选择哪种解决方案取决于具体需求和场景。无论是通过手动提交事务、使用特定的事务传播行为还是使用异步调用,都可以确保数据的一致性和事务的完整性。

相关推荐
say_fall1 小时前
【AI大模型】MCP 模型上下文协议:客户端-服务器架构、JSON-RPC 消息与能力协商、工具发现与调用链路、与函数调用的分工
服务器·人工智能·架构
学长毕设1 小时前
基于SpringBoot的野生动物公益保护平台设计与实现-附源码
java·spring boot·后端
Decay__2 小时前
14_MySQL 高可用与容灾架构 —— MGR / InnoDB Cluster / Keepalived+VIP / 双主 / 读写分离 / 两地三中心
运维·数据库·sql·mysql·架构
SL_staff2 小时前
离散制造排程破局:JVS-APS 四步闭环落地实践(开发者视角)
java·产品·全栈
这个昵称不容易2 小时前
SpringBoot 美容院管理系统:232个Java文件315个接口,5套角色从预约到发货一整条链路
java·springboot·毕设
猿人谷2 小时前
Surrogate Model:让进化算法学会“预测”,为什么它正在成为智能优化的重要技术?
后端·算法·架构
她的男孩2 小时前
三人会签只批了一个就往下走:Forge Admin 把 all / any / ratio 写成 completionCondition
spring boot·低代码·工作流引擎
Kari113 小时前
企业级智能体平台怎么选:腾讯云ADP与其他主流技术路线的架构对比
架构·云计算·腾讯云
泯泷3 小时前
AI时代,中层还要替公司传多少次话?
人工智能·架构
装不满的克莱因瓶3 小时前
【SpringAI Alibaba】ReactAgent记忆管理MessageTrimmingHook与MessageDeletionHook
java·人工智能·ai·ai编程·springai·springaialibaba