Seata做分布式事务时,报错后事务不回滚的问题

1、Seata做分布式事务时,报错后事务不回滚的问题?
具体问题举例:有两个服务A和服务B,服务A通过openfiegn调用服务B的接口,服务A和服务B都能正常注册到seat服务。接口正常添加@GlobalTransactional注解。方法报错是单个服务内事务均能回滚。但当服务A的serviceA调用服务B的serviceB时,serviceB执行成功并返回结果到serviceA中,然后serviceA报错,些时serviceA能回滚,serviceB不能回滚。

serviceA:

    @Override
    @GlobalTransactional
    public void createTeacherAndStudent() {
        System.out.println("事务id---------------------->" + RootContext.getXID());
        logger.info("========= 开始调用TEACHER");
        Teacher teacher = new Teacher();
        teacher.setName("卫国老师");
        teacher.setAge(58);
        teacher.setEmail("weiguo@teacher.com");
        mapper.add(teacher);
        mapper.delete(11L);

        // 抛出异常
        logger.info("========= 开始调用Studen");
        logger.info("========= Student将会出现异常");
        StudentVo studentVo = new StudentVo();
        studentVo.setName("分布式学生");
        studentVo.setAge(10);
        studentVo.setEmail("1@qq.com");
        studentVo.setCreateTime(new Date());

        JsonNode add = studentService.addV2("分布式",10,"1@11.com");
        logger.info("======== 接收到学生服微服务返回内容:{}", add.toString());
        int code = add.get("code").asInt();
        String msg = add.get("msg").asText();
        // 可以抛出异常  也可以不抛出异常
        // 本系统调用其他微服务  其他微服务抛出异常  也会触发分布式事务
        if (code != 200) {
            throw new BusinessException(code, msg);
        }
    }

运行后事务ID打印:

事务id---------------------->10.100.6.183:8091:6755797768365363267

serviceB:

   @Override
   //作为被调用方,加不加这个注解,全局事务都会生效。
   @GlobalTransactional
    public Long create(StudentVo studentVo) {
        System.out.println("事务id---------------------->" + RootContext.getXID());
        // 将VO对象转换为POJO对象
        BaseConverter<StudentVo, Student> baseConverter = new BaseConverter<>();
        Student student = baseConverter.convert(studentVo, Student.class);
        mapper.insert(student);
        return student.getId();
    }

事务id打印结果:

事务id---------------------->10.100.6.183:8091:6755797768365363269

可以看出两次事务id不一致,这就说明两次数据库操作并不是在一个全局事务中,所以造成了问题。我们的问题变为为什么事务id不一致?

一:seat全局事务id是在放在heard中在服务间传递的,然而通过断点,确实在服务B的请求request.header中有事务id。但为什么拿不到?

二: 查看RootContext.getXID()方法源码,发现是从线程变量中取的值,如果没有取到则会新开户一个全局事务。那么问题变成了设置本次请求初始化时设置线程变量出了问题。

三:通过断点分析,正常情况下,服务器接收到请求后,会通过一系列的拦截器列表,其中就有SeataHandlerInterceptor。然而我服务A和服务B启动时初始化的拦截器中根本就没有Seata这个拦截器。那一定是seata配置出了问题,我来来回回对比配置N次就是没有发现配置有什么问题。

四:继续分析Springboot源码,最终发现,正常情况下初始化拦截器会调用DelegatingWebMvcConfiguration 这个类的addInterceptors方法:

    protected void addInterceptors(InterceptorRegistry registry) {
        this.configurers.addInterceptors(registry);
    }

而我的服务启动却执行的:WebMvcConfigurationSupport类的方法,而这个方法是空方法。而这个类是DelegatingWebMvcConfiguration 的父类。

    protected void addInterceptors(InterceptorRegistry registry) {
    }

五:最终我发现我的config包里面有几个config类,其中一个是WebMvcConfig.java类。这个类只是用来配置swagger的,而这个类正好是继承至:WebMvcConfigurationSupport。我怀疑就是这里搞的鬼,果不其然,我把WebMvcConfigurationSupport 替换为DelegatingWebMvcConfiguration 后,终于看到两个两个服务都事务回滚了。


@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport{

    @Resource
    ConstantProperties constantProperties;

    /**
     * 静态资源配置
     * @param registry
     * @author
     * @createTime 2020/07/14
     * @description
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        String apiDocStatus = "true";
        if(apiDocStatus.equalsIgnoreCase(constantProperties.API_DOC)){
            // doc文档接口
            registry.addResourceHandler("/doc/**").addResourceLocations("classpath:/doc/");
            // swagger静态资源配置
            registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
            super.addResourceHandlers(registry);
        }
    }
}

另一种是实现WebMvcConfigurer接口

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Resource
    ConstantProperties constantProperties;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        String apiDocStatus = "true";
        if(apiDocStatus.equalsIgnoreCase(constantProperties.API_DOC)){
            // doc文档接口
            registry.addResourceHandler("/doc/**").addResourceLocations("classpath:/doc/");
            // swagger静态资源配置
            registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        }
    }
}

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wolf犭良

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值