106:Seata框架底层深度源码解析

  • 1 回顾seata解决分布式事务的问题
  • 2 seata基于undo_log表逆向生成sql语句回滚
  • 3 setata使用前置镜像和后置镜像保存日志信息
  • 4 seata根据undo_log日志逆向回滚原理
  • 5 seata全局事务注解入口源码类分析
  • 6 GlobalTransactionalInterceptor源码分析
  • 7 TM如何远程连接TC获取全局事务id
  • 8 Seata前置和后置镜像源码深度分析
  • 9 Seata发起方调用完接口后发生回滚还是提交


1 回顾seata解决分布式事务的问题

课题内容

  1. Seata到底和LCN有哪些区别?
  2. Seata中undo_do日志到底有何作用?
  3. Seata中的如何逆向生成sql回滚?
  4. Seata深度源码分析总结

2 seata基于undo_log表逆向生成sql语句回滚

seata解决分布式事务使用逆向生成sql语句回滚,undo_log记录每次的sql执行语句,可能产生脏读,但是可以避免死锁。

场景演示:

spring boot 集成pgsql 插入后获取主键id_java


LCN参与方等待协调者通知结果过程中一直阻塞等待,此时数据库有begin操作没有commit或者rollback发生行锁,一旦协调者或者发起方宕机,参与方将一直等待从而产生死锁。

3 setata使用前置镜像和后置镜像保存日志信息

Seata实现原理

  1. TM(发起方)连接到TC事务协调者,创建一个全局的事务id(xid),保存到ThreadLoacl中;
  2. TM(发起方)和RM(参与方)都被Seata的数据源实现代理,在原生的sql执行之前和之后保存原来的和修改后的结果到undo_log日志中,方便后期实现回滚;

前置镜像和后置镜像
分别表示sql执行之前数据库中的数据结果和执行之后的结果。
select * from orderId=1 state=0 ----前置镜像
update order set state=1 where orderId=1
select * from orderId=1 state=1 ----后置镜像
将前置镜像和后置镜像保存到undo_log日志中
回滚:
update order set state=0 where orderId=1 —还原

4 seata根据undo_log日志逆向回滚原理

  1. TM(发起方)使用feign客户端调用接口的时候,在ThreadLoacl中获取xid,设置到请求头中;
  2. RM(参与方)从请求中获取到该xid,设置到ThreadLoacl中,同时也会向Seata server注册该分支事务;
  3. TM(发起方)将当前本地事务的结果告诉给协调者TC,协调者TC再通知所有的分支是否回滚;
  4. TM(发起方)如果调用接口成功后抛出异常的情况下,告诉给协调者TC,协调者TC再通知所有的分支根据全局的xid和分支事务的id查询分支数据源的undo_log日志表,逆向生成sql语句实现回滚,同时删除对应的undo_log日志;
  5. TM(发起方)如果调用接口成功后没有抛出任何的异常,告诉给协调者TC,协调者TC再通知所有的分支根据全局的xid和分支事务的id删除对应的undo_log日志;

如何逆向实现sql语句:
insert 逆向 delete
delete逆向insert
update逆向update(前置镜像和后置镜像)

Seata与Lcn的区别
基本实现的思路是一样的,唯一区别在于回滚的方式
LCN采用代理数据源假关闭连接,暂时不提交本地事务,但是容易造成数据的死锁。
Seata采用undo_log的形式逆向生成sql语句实现回滚,避免死锁现象但是容易出现脏读。

5 seata全局事务注解入口源码类分析

发起方在方法上面加上@GlobalTransactional,被aop拦截。
Aop的入口分析:

  1. 项目中会引入spring-cloud-alibaba-seata.jar执行spring.factories配置,读取GlobalTransactionAutoConfiguration配置类,会加载全局事务扫描器(GlobalTransactionScanner)到Spring的容器中
  2. GlobalTransactionScanner实现了AbstractAutoProxyCreator、InitializingBean
    AbstractAutoProxyCreator:SpringAOP原生类 创建代理对象
    InitializingBean:SpringBean生命周期初始化
    GlobalTransactionScanner:对象初始化成功之后开始注册RM和TM到TC中

6 GlobalTransactionalInterceptor源码分析

AbstractAutoProxyCreator类中有一个回调的方法wrapIfNecessar,创建GlobalTransactionalInterceptor。

执行发起方方法前会执行GlobalTransactionalInterceptor的invoke方法,判断方法上是否有加上@GlobalTransactional注解,再执行transactionalTemplate的execute方法。

spring boot 集成pgsql 插入后获取主键id_协调者_02

7 TM如何远程连接TC获取全局事务id

TM流程获取全局事务id流程:
通过当前线程获取xid,如果没有创建默认的全局事务对象GlobalTransaction(里面xid字段为空),事务开启后远程连接TC返回结果中获取xid再设置到threadlocal中。

8 Seata前置和后置镜像源码深度分析

spring boot 集成pgsql 插入后获取主键id_java_03

9 Seata发起方调用完接口后发生回滚还是提交

A服务调用B服务接口成功后A服务宕机,B服务已经提交数据库是否需要回滚?

Seata有设置超时时间,在一定时间内如果发起方TM没有告诉TC协调者是否要回滚,参与方RM(B服务)默认自动进行回滚。

数据库seata库branch_table表和global_table表记录该条数据,待A项目再次启动后自动回滚A的数据,同时删除branch_table表和global_table表的记录。

spring boot 集成pgsql 插入后获取主键id_seata_04