Java开发笔记

Seata 2.0自定义异常变try to proceed invocation error问题

2024/08/14
loading

在spring cloud 2023.0.2, seata2.0版本中会遇到如下问题:

业务逻辑抛出自定义异常后,上层捕获不到业务原始异常信息,只能看到类似:

java.lang.RuntimeException: try to proceed invocation error
at io.seata.spring.annotation.AdapterInvocationWrapper.proceed(AdapterInvocationWrapper.java:59)
at io.seata.integration.tx.api.interceptor.handler.GlobalTransactionalInterceptorHandler$2.execute(GlobalTransactionalInterceptorHandler.java:200)
at io.seata.tm.api.TransactionalTemplate.execute(TransactionalTemplate.java:128)
at io.seata.integration.tx.api.interceptor.handler.GlobalTransactionalInterceptorHandler.handleGlobalTransaction(GlobalTransactionalInterceptorHandler.java:197)
at io.seata.integration.tx.api.interceptor.handler.GlobalTransactionalInterceptorHandler.doInvoke(GlobalTransactionalInterceptorHandler.java:166)
at io.seata.integration.tx.api.interceptor.handler.AbstractProxyInvocationHandler.invoke(AbstractProxyInvocationHandler.java:35)
at io.seata.spring.annotation.AdapterSpringSeataInterceptor.invoke(AdapterSpringSeataInterceptor.java:45)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)

即便你在业务层抛出了具体的异常(如 BusinessException),Seata 在拦截并回滚后 只包装成了 RuntimeException,导致原始异常信息丢失,这给统一异常处理、返回友好错误提示带来了很大困扰。

问题描述与重现场景

在一个常见的 Spring Cloud + Seata 分布式事务场景里:

@GlobalTransactional
public void createOrder(...) {
    // 业务检查
    if(stockNotEnough()) {
        throw new BusinessException("库存不足");
    }

    // 其他业务...
}

运行后,服务回滚成功,但输出弱化成:

java.lang.RuntimeException: try to proceed invocation error

而不是看到原始的 "库存不足" 异常提示或堆栈。此时前端只会拿到 RuntimeException 信息。

原因分析

这是 Seata 2.0 特有的问题

Seata AOP/拦截器机制把内部异常封装得过早

Seata 通过内部拦截器代理业务方法:

AdapterInvocationWrapper.proceed(...)

这是 Seata 在调用目标业务方法时的一个 wrapper。在捕获业务异常后,它会统一抛出:

RuntimeException: try to proceed invocation error

而原始异常被作为 Cause 记录,但 上层捕获只有 RuntimeException,导致业务层异常被“吞掉”。

社区确认:这是 Seata 2.0 的 Bug

在 Seata 官方的 Issues 中,有相关讨论:https://github.com/apache/incubator-seata/issues/6622

社区确定后续版本将修复这个问题,也就是说这是一个 已知缺陷,而不是你写代码的问题

解决方案

降级版本至seata1.8,实测可行

CATALOG