Seata全局事务异常情况总结

49 阅读2分钟

1. 前置准备

1.1 数据表准备

CREATE TABLE `t1` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

CREATE TABLE `t2` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

1.2 相关代码准备

1.2.1 T1实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "t1")
public class T1 {

    @Id
    @Column(name = "id", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name", nullable = false)
    private String name;
}

1.2.2 T2实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "t2")
public class T2 {

    @Id
    @Column(name = "id", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name", nullable = false)
    private String name;
}

1.2.3 dao接口定义

public interface T1Dao extends JpaRepository<T1, Long> {
}
public interface T2Dao extends JpaRepository<T2, Long> {
}

1.2.4 service编写

@Slf4j
@Service
public class T1Service {

    @Autowired
    private T1Dao t1Dao;

    public void save() {
        T1 t1 = new T1();
        t1.setName("t1");
        t1Dao.save(t1);
    }
}
@Slf4j
@Service
public class T2Service {

    @Autowired
    private T2Dao t2Dao;

    public void save() {
        T2 t2 = new T2();
        t2.setName("t2");
        t2Dao.save(t2);
    }
}

1.2.5 controller编写

@RestController
@RequestMapping("/t1")
@SuppressWarnings("rawtypes")
public class T1Controller {

    @Autowired
    private T1Service t1Service;

    @GetMapping
    public CommonResponse save() {
        t1Service.save();
        return CommonResponse.ok();
    }
}
@RestController
@RequestMapping("/t2")
@SuppressWarnings("rawtypes")
public class T2Controller {

    @Autowired
    private T2Service t2Service;

    @GetMapping
    public CommonResponse save() {
        t2Service.save();
        return CommonResponse.ok();
    }
}

1.2.6 在事务发起的服务定义Feign接口

@GetMapping("/assets/t1")
CommonResponse save();
@GetMapping("/vip/t2")
CommonResponse save();

1.2.7 TM编写

@Slf4j
@Service
public class TestService {

    @Autowired
    private VipFeignClient vipFeignClient;

    @Autowired
    private AssetsFeignClient assetsFeignClient;

    @GlobalTransactional
    public void save() {
        assetsFeignClient.save();
        log.info("wallet save completed");
        vipFeignClient.save();
    }
}

2. 异常演示

2.1 资产服务调用成功 但会员服务迟迟不调用

image.png

2.1.1 执行过程中数据的变化

image.png

image.png

image.png

2.1.2 执行结果

vip服务超时出现异常,已执行的事务操作被回滚

image.png

2.2 资产服务调用成功,但TM挂了

2.2.1 执行过程中数据的变化

image.png

image.png

image.png

2.2.2 执行结果

参与全局事务的RM执行commit操作,数据保存成功

image.png

image.png

2.3 调用完资产服务后,事务未结束时,资产服务挂了

2.3.1 执行结果

数据保存成功,参与全局事务的服务执行commit操作。

image.png

image.png

image.png

image.png

2.4 调用完资产服务后,事务未结束,会员服务挂了

2.4.1 执行结果

由于会员服务挂了,导致调用失败,所以回滚全局事务

image.png

2.5 如果处于调用过程中,TC 挂了

2.5.1 执行结果

已执行完的数据会暂时保存在数据表中,对应的undo_log也会保存,参与分布式事务的服务会不断重试 尝试连接seata-server,seata-server重启后参与分布式事务的服务会重新连接上,并 回滚 对应的数据。