方式一:
配置了Seata的模式为XA,并且在submitNews方法上添加了@GlobalTransactional注解,表明开启分布式事务。该方法表示 发布修改文章或保存为草稿。调用了saveOrUpdateWmNews()方法
该方法表示保存或修改文章,由于是使用的MP,所以保存后会返回主键id
保存完毕后,此处调用了wmNewsAutoScanService.autoScanWmNews()方法
autoScanWmNews()方法是自媒体文章审核,@Async注解标识该方法是异步方法。当请求到达这个方法时,可以看出saveOrUpdateWmNews()方法保存文章后生成的主键ID已经被传过来,但是由于开启了XA模式的分布式事务,mp的保存方法虽然被执行但是实际的数据尚未被保存至Mysql数据库中,所以这里虽然接受到了主键ID,但是却无法从数据库中查找到该数据。此处执行if语句抛出异常。
虽然抛出了异常,但是这里可以看出,分布式事务将已经执行的代码进行了提交,没有回滚。
并且此处虽然抛出了异常,但是由于@Async注解的存在,该异常没有被全局异常处理器捕获。
补充说明:
在此处打印分布式事务的唯一标识Xid以及线程名称可以看出,执行saveOrUpdateWmNews()方法和autoScanWmNews()异步方法的线程及Xid不一致
方式二:
将@Asycn注解注释掉,此时可以正常提交回滚。异常可以被全局异常处理器捕获
方式三:
将@Async @GlobalTransactional都放在autoScanWmNews()方法上,此时分布式事务可以被正常提交和回滚。(该方法抛出的异常无法被全局异常处理器捕获)
文章只会回滚@GlobalTransactional注解标记下的autoScanWmNews()方法以及该方法里面调用的其他方法涉及的那部分数据
总结:
当使用Seata分布式事务的XA模式时,最好将@Async @GlobalTransactional放在同一个方法上,否则分布式事务可能出现错误。根本原因是执行@Async异步方法时会另外开启一条单独的线程。
如果在@Async注解下的方法抛出了异常,那么该异常无法被全局异常处理器捕获。如果@Async注解下的方法调用了其他微服务的某个方法,并且此方法抛出异常,那么此方法抛出的异常可以被自己微服务本身的全局异常处理器捕获。