seata和全局异常处理导致事务无法回滚的解决方案

方案一:全局异常处理类捕获到异常判断回滚

在您的订单模块代码中,库存扣减失败后被全局异常处理捕获并返回统一的 Result JSON 结果,导致 Feign 调用无法捕获到库存扣减失败的异常,从而影响 Seata 分布式事务的回滚。

为了解决这个问题,您可以在全局异常处理类中,针对特定的异常类型(如库存扣减失败的异常),手动触发 Seata 的全局事务回滚。

以下是修改后的全局异常处理类示例:

package com.atguigu.storage.handler;
​
import com.atguigu.storage.result.Result;
import com.atguigu.storage.result.ResultCodeEnum;
import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.GlobalTransactionContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
​
@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
public class GlobalExceptionHandler {
​
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e) {
        e.printStackTrace();
        System.err.println("触发异常拦截: " + e.getMessage());
​
        // 判断是否为库存扣减失败的异常
        if (e.getMessage().contains("扣减库存失败")) {
            // 获取全局事务 XID
            String xid = RootContext.getXID();
            if (xid != null) {
                try {
                    // 手动回滚全局事务
                    GlobalTransactionContext.reload(xid).rollback();
                } catch (TransactionException ex) {
                    ex.printStackTrace();
                }
            }
        }
​
        return Result.fail();
    }
​
    @ExceptionHandler({IllegalArgumentException.class})
    @ResponseBody
    public Result llegalArgumentException(Exception e) {
        System.err.println("触发异常拦截: " + e.getMessage());
        return Result.build(null, ResultCodeEnum.ARGUMENT_VALID_ERROR);
    }
}

在上述代码中,当捕获到异常时,首先判断异常信息中是否包含“扣减库存失败”字样,如果是,则获取当前的全局事务 XID,并手动触发回滚操作。

请注意,这种方式需要您在异常信息中明确标识出库存扣减失败的情况,以便在全局异常处理类中进行判断。

此外,您还可以考虑在 Feign 的 @FeignClient 注解中,使用 fallbackfallbackFactory 属性来处理远程调用失败的情况,并在降级方法中手动触发全局事务回滚。

例如,使用 fallback 属性指定降级实现类:

@FeignClient(name = "storage-service", fallback = StorageClientFallback.class)
public interface StorageClient {
    // ...
}

在降级实现类 StorageClientFallback 中,您可以手动触发全局事务回滚。

请根据您的具体业务需求,选择合适的方式来处理库存扣减失败的异常,以确保 Seata 分布式事务能够正确回滚。

方案二:切面类捕获service业务代码异常手动回滚

package com.atguigu.handler;
​
import java.lang.reflect.Method;
​
​
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
​
import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.tm.api.GlobalTransaction;
import io.seata.tm.api.GlobalTransactionContext;
​
@Aspect
@Component
public class TestAspect {
    private final static Logger logger = LoggerFactory.getLogger(TestAspect.class);
    
​
​
    //方式二
    @AfterThrowing(throwing = "e", pointcut = "execution(* com.atguigu.*.service.*.*(..))")
    public void doRecoveryActions(Throwable e) throws TransactionException {
        logger.info("方法执行异常:{}", e.getMessage());
        if (!StringUtils.isBlank(RootContext.getXID()))
            GlobalTransactionContext.reload(RootContext.getXID()).rollback();
    }
​
​
​
}

方式三:直接在全局异常继续抛出异常

java

@Override
    public void deduct(String code, Integer count) {
        log.info("开始扣减库存");
        try {
            LambdaUpdateWrapper<Storage> update = new LambdaUpdateWrapper<>();
            update.eq(Storage::getCommodityCode, code);
            update.setSql("count = count - " + count);
            this.update(update);
        } catch (Exception e) {
            throw new IllegalArgumentException("扣减库存失败,可能是库存不足!", e);
        }
        log.info("扣减库存成功");
        
package com.atguigu.handler;
import com.atguigu.reuslt.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
​
​
/**
 * 全局异常处理类
 *
 */
​
@ControllerAdvice
public class GlobalExceptionHandler {
​
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        System.err.println("触发异常拦截: " + e.getMessage());
        //方式一
        // 获取全局事务 XID
//        String xid = RootContext.getXID();
//        if (xid != null) {
//            try {
//                // 手动回滚全局事务
//                GlobalTransactionContext.reload(xid).rollback();
//            } catch (TransactionException ex) {
//                ex.printStackTrace();
//            }
//        }
        return Result.fail();
    }
​
​
    @ExceptionHandler({IllegalArgumentException.class})
    @ResponseBody
    public Result llegalArgumentException(Exception e) {
        System.err.println("触发异常拦截: " + e.getMessage());
        throw new RuntimeException(e.getMessage());
//        return Result.build(null, ResultCodeEnum.ARGUMENT_VALID_ERROR);
    }
​
}
​

## 方式四:获取feign结果判断result响应码是否为200

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值