当我们在一个方法上加了@GlobalTransactional注解后,会被GlobalTransactionScanner类扫描到,然而GlobalTransactionScanner继承了AbstractAutoProxyCreator,重写了wrapIfNecessary方法,作用是会生成一个代理对象,在执行目标方法前处理一些其他的逻辑。
在执行目标方法之前会被GlobalTransactionalInterceptor全局事务拦截器所拦截执行GlobalTransactionalInterceptor.invoke方法。
主要逻辑:
beginTransaction(txInfo, tx);
开启全局事务,发送消息到seata服务端,seata服务端向global_table表中插入一条数据。
business.execute();
执行目标方法,比如下代码
@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)public void create(Order order) {LOGGER.info("------->交易开始");//本地方法orderDao.create(order);//远程方法 扣减库存storageApi.decrease(order.getProductId(),order.getCount());//远程方法 扣减账户余额LOGGER.info("------->扣减账户开始order中");accountApi.decrease(order.getUserId(),order.getMoney());LOGGER.info("------->扣减账户结束order中");int i=10/0;LOGGER.info("------->交易结束");}
上面的create方法中的每个方法都会执行以下逻辑
- 执行前置镜像(只有修改和删除有前置镜像),所谓前置对象就是方法执行前的记录;
- 执行真正的sql(业务逻辑),但是此时不会提交;
- 执行后置镜像(删除没有后置镜像),所谓后置镜像就是执行了上面的业务逻辑后的记录;
- 将前置对象和后置对象的结果封装成一个undoLog对象,插入数据库,此时也没有提交;
- 注册分支事务,向branch_table表中插入一条数据,并且向lock_table表中插入一条行锁,防止其他事务修改这条记录;
- 提交事务,并向seata服务端报告状态(成功或者失败)。
当所有的分支事务都执行完了并且没有抛出异常,会执行以下逻辑:
- 全局事务的发起方会seata服务端申请提交这个全局事务,seata服务端会根据xid查询数据库中的global_table、branch_table表,删除与branch_table相关联的lock_table中的数据,释放锁;
- 根据xid将global_table表中的对应的数据的事务状态改为“Committing”,紧接着将事务状态改为“AsyncCommitting”;
- 此时seata服务端开启了一个定时器,定时查询所有事务状态为“AsyncCommitting”的记录,查询之后,根据查询出来记录的xid查询branch_table表;
- 循环所有分支事务,通知客户端删除undo_log表中的记录,客户端删除成功的话,再删除此次循环的分支事务数据,循环所有分支事务完毕后,根据xid修改global_table表中的对应的数据的事务状态为“Committed”,最后删除global_table表中的记录。
当上面的分支事务其中有一个分支事务执行失败,会执行以下逻辑:
- 全局事务的发起方会向seata服务端申请回滚,seata服务端会根据xid查询数据库中的global_table、branch_table表,删除与branch_table相关联的lock_table中的数据,释放锁;
- 根据xid将global_table表中的对应的数据的事务状态改为“Rollbacking”,循环所有分支事务,通知客户端回滚,客户端通过undo_log中的记录,反序列化成前后置对象,生成反向sql,执行回滚;
- 循环所有分支事务完毕后,根据xid修改global_table表中的对应的数据的事务状态为“Committed”,最后删除global_table表中的记录。
