问题描述
最近系统中准备使用seata作为分布式事务的工具,但是遇到了个很棘手的问题,正常的service中报错是没有什么问题的,事务都可以进行正常的回滚,但是一旦feign报错,便会自动降级,seata便无法捕获异常。
根据上面的问题,我测试了将下图这段配置降级的代码删掉,结果事务回滚成功!!!
feign:
sentinel:
enabled: true
但是feign降级作为feign使用的重中之重,不可能不去使用啊,所以只能想办法解决了。
解决办法
参考了网上一位大神的例子,说用Spring AOP来进行手动开启全局事务ID和事务回滚,代码如下
/**
* @author jxys
* @since 2020年6月8日09:37:55
* 类描述: 用于处理程序调用发生异常的时候由于异常被处理以后无法触发事务,而进行的处理,使之可以正常的触发事务。
*/
@Aspect
@Component
public class WorkAspect {
private final static Logger logger = LoggerFactory.getLogger(WorkAspect.class);
//io.seata.saga.engine.invoker
@Before("execution(* com.jxysgzs.service.local.*.*(..))")
public void before(JoinPoint joinPoint) throws TransactionException {
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
tx.begin(300000, "test-client");
logger.info("**********创建分布式事务完毕" + tx.getXid());
}
@AfterThrowing(throwing = "e", pointcut = "execution(* com.jxysgzs.service.local.*.*(..))")
public void doRecoveryActions(Throwable e) throws TransactionException {
logger.info("方法执行异常:{}", e.getMessage());
if (!StringUtils.isBlank(RootContext.getXID())) {
GlobalTransactionContext.reload(RootContext.getXID()).rollback();
}
}
}
这篇代码的大致意思是说,当系统进入service的某个具体实现方法A后,会被before
方法捕获并生成一个全局事务ID,这和seata是一样的。当A方法中出现报错后,系统捕获异常,进入到doRecoveryActions
方法,通过全局事务ID进行回滚。
大致这么一看好像没啥问题,但是我自己试了试。。然并卵。除了基本的报错可以回滚,被降级的报错并没有回滚。
这里的问题在于,无论是降级还是像try/catch这种自己化解并处理的报错都不会抛出去,doRecoveryActions
方法也就没办法捕获,所以我们需要手动调用一下这个方法,像我下面这个dome中使用的。
@Component
public class UserServiceFallbackFactory implements FallbackFactory<UserService> {
@Resource
WorkAspect workAspect;
@Override
public UserService create(Throwable throwable) {
try {
//手动调用全局事务回滚
workAspect.doRecoveryActions(throwable);
} catch (TransactionException e) {
e.printStackTrace();
}
System.out.println("进行降级");
return id -> new JcRy();
}
}
上图便是fegin的降级方法,在这里调用一下事务回滚也可以进行全局事务回滚。
目前最好的处理方法我只能想到这里了,这个方法有个很大的弊端就是代码量太大,每个方法每个class都需要引入+调用。好处就是这样写能解决基本问题而且目前经过我的测试还没有发现什么bug,只不过在service的一个方法中,如果同时出现多个fegin调用的情况是不可以正常使用的,事务只会回滚报错feign以上执行的操作。希望能帮到各位同学。