全局异常处理导致seata分布式事务无法回滚问题,有人用AOP的方法解决过吗,多个服务怎么弄?
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
使用AOP手动开启全局事务并进行回滚。
@Aspect
@Component
@Slf4j
public class WorkAspect {
    @Before("execution(* com.trs.slivlooncloud.service.*.*(..))")
    public void before(JoinPoint joinPoint) throws TransactionException {
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
        tx.begin(300000, "tran");
        log.info("**********创建分布式事务完毕 {}" , tx.getXid());
    }
    @AfterThrowing(throwing = "e", pointcut = "execution(* com.trs.slivlooncloud.service.*.*(..))")
    public void doRecoveryActions(Throwable e) throws TransactionException {
        log.info("方法执行异常:{}", e.getMessage());
        if (!StringUtils.isBlank(RootContext.getXID())) {
            GlobalTransactionContext.reload(RootContext.getXID()).rollback();
        }
    }
}
——参考来源于SEATA官方文档。
全局异常处理导致 Seata 分布式事务无法回滚的问题,可以使用 AOP(面向切面编程)的方法进行解决。在 SpringBoot 项目中,可以通过创建一个切面类,使用 @Around 注解环绕通知方法,捕获全局异常,然后通过 Seata 的 API 进行事务回滚。
以下是一个简单的示例:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class GlobalExceptionHandlerAspect {
    @Pointcut("@annotation(com.example.globalexception.annotation.GlobalException)")
    public void pointcut() {
    }
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            return joinPoint.proceed();
        } catch (Exception e) {
            // 处理全局异常
            handleGlobalException(e);
            // 进行事务回滚
            return null;
        }
    }
    private void handleGlobalException(Exception e) {
        // 记录日志等操作
        // ...
    }
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GlobalException {
}
@Service
public class SomeService {
    @GlobalException
    public void someMethod() {
        // ...
    }
}
这样,在执行带有 @GlobalException 注解的方法时,如果发生全局异常,切面类中的 handleGlobalException 方法会被调用,然后通过 Seata 的 API 进行事务回滚。
AOP可以让你在不修改原有代码的情况下,对方法进行增强或者对全局异常进行统一处理。
在Seata分布式事务中,如果某个服务出现异常,可以通过AOP来捕获这个异常,然后进行相应的处理,以保证分布式事务的正确性。
下面是一个简单的示例,演示如何使用AOP来处理全局异常:
1.定义一个注解,用于标记需要拦截的方法:
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
public @interface Transactional {  
}
2.创建一个AOP切面,实现MethodInterceptor接口:
@Aspect  
@Component  
public class TransactionalAspect implements MethodInterceptor {  
    @Around("@annotation(Transactional)")  
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {  
        try {  
            return joinPoint.proceed(); // 执行方法  
        } catch (Exception e) {  
            // 处理异常,例如记录日志、回滚事务等  
            throw e; // 抛出异常,以触发Seata分布式事务回滚  
        }  
    }  
}
3.在需要使用@Transactional注解的方法上添加该注解:
@Service  
public class MyService {  
    @Transactional  
    public void myTransactionalMethod() {  
        // 执行分布式事务操作  
    }  
}
4.如果在myTransactionalMethod()方法中发生了异常,AOP切面会捕获到这个异常,并抛出给Seata分布式事务管理器,从而触发回滚事务。
5.对于多个服务的情况,只需要在每个服务中重复上述步骤即可。确保每个服务中的方法都使用@Transactional注解,并配置相应的AOP切面。这样,无论哪个服务发生异常,Seata分布式事务都可以正确回滚。
在微服务架构下,使用Seata进行分布式事务管理可能会遇到一些挑战。当全局异常处理导致Seata无法正确回滚事务时,这通常发生在使用了AOP全局异常处理的场景中。具体而言,如果一个事务(例如A事务)调用了另一个事务(例如B事务),并且B事务出现了异常,那么即使在A事务中设置了事务回滚,也可能导致事务回滚失败。
这是因为当全局异常被内部处理(比如通过自定义的全局异常AOP拦截器)后,Seata不会再处理这个异常信息,从而导致事务回滚失败。
解决方案有几个:
手动回滚:在捕获到异常后,在代码中手动进行事务回滚。
避免全局异常拦截:确保所有参与分布式事务的接口不被全局异常AOP拦截。这样,当异常发生时,Seata能够正确地处理它并执行相应的回滚操作。
配置Seata:确保Seata服务已经正确配置和启动。如果Seata服务和微服务不在同一个局域网内,需要指定IP和端口号来启动Seata服务。