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 注解中,使用 fallback 或 fallbackFactory 属性来处理远程调用失败的情况,并在降级方法中手动触发全局事务回滚。
例如,使用 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
更多推荐


所有评论(0)