Seata 全局事物切面

Seata 的事物注解是 GlobalTransactional , 可以通过调用标记这个注解的方法,开启一个分布式事务。
Seata 的声明方式与Spring事物声明方式类似,相比Spring定义的事物声明,加入了事物名称和事物超时时间,传播机制取消了嵌套类型(Propagation.NESTED)。

首先看全局事物的声明和调用方式

全局事物注解

public @interface GlobalTransactional {
    /**   事物名称, 必须唯一 */
    String name() default "";
   	// 默认 60 秒超时
	int timeoutMills() default TransactionInfo.DEFAULT_TIME_OUT;
	// 传播机制
	Propagation propagation() default Propagation.REQUIRED;
	// 其他和 spring 事物一样的属性就省略了
}

调用方式

@GlobalTransactional(timeoutMills = 300000,  name = "seata-example")
public ObjectResponse handleBusiness(BusinessDTO businessDTO) {
	  //1、扣减库存 
	  ObjectResponse storageResponse = 
	  		storageDubboService.decreaseStorage(commodityDTO);
	// 2. 生成订单
	  ObjectResponse<OrderDTO> response = 
	  		orderDubboService.createOrder(orderDTO);
	 	// 省略处理响应的内容
}

Seata 事物切面

全局事物切面的作用

事物切面在事物开始前向事务协调者(TC)发送全局事物开启请求, 获得XID。 把XID通过微服务调用其他的服务时透传过去。其他微服务通过XID处理分支事物。
微服务调用完成时, 由事务管理器(TM)向事物协调者(TC)发起全局事物提交请求, TC 会向每个注册了分支事物的微服务发起分支事物提交请求(AT模式就是删除回滚日志undo_log).
微服务调用失败时, 由事务管理器(TM)向事物协调者(TC)发起全局事物回滚请求, TC 会向每个注册了分支事物的微服务发起分支事物回滚请求(AT模式就是查询回滚日志, 对比当前数据和后置镜像是否一致的方式避免脏写, 如果没有脏写,就用前置镜像构建SQL语句,然后执行SQL语句回滚数据).

全局事物切面的实现方式

切面的扫描由 GlobalTransactionScanner 定义, GlobalTransactionScanner 扩展了 Spring 的 BeanPostProcessor 接口.

当GlobalTransactionScanner创建完成后,会调用初始化方法, 初始化资源管理器, 初始化事物管理器, 注册容器关闭的回调函数。

由于实现了 BeanPostProcessor, 所以对于所有Bean的创建,在bean初始化的时候都可以进行增强。

GlobalTransactionScanner 实现了很多接口,这里关注其中几个重要的接口
类图

  1. BeanPostProcessor , Spring 对Bean增强的处理
  2. DisposableBean , 容器关闭时, 执行所有的回调函数
  3. InitializingBean 初始化接口, 初始化资源管理器, 事务管理器, 注册关闭容器的回调函数
  4. Ordered 排序, 默认值为 1024
  5. AopInfrastructureBean, 标记当前Bean是基础设施, 基础设施不会被增强

当 GlobalTransactionScanner 扫描当前类的所有方法声明,和所有接口的方法声明 找到有 @GlobalTransactional 或者 @GlobalLock 就对当前bean进行增强, 加入指定的增强器: GlobalTransactionalInterceptor

AOP加载流程

切面顺序

AOP 排序是一个值得讨论的问题
在这里举一个例子, 对于一个方法同时有三种注解, @Async,@GlobalTransactional,@Transactional 会发生什么情况?
这里要从BeanPostProcessor的角度来描述

这三个注解由不同的BeanPostProcessor处理

  1. 处理 @Async 的是 AsyncAnnotationBeanPostProcessor,顺序为 Ordered.LOWEST_PRECEDENCE(最低优先级)
  2. 处理 @Transactional的是标准AOP , 顺序为 Ordered.HIGHEST_PRECEDENCE(最高优先级)
  3. 处理 @GlobalTransactional 的是 GlobalTransactionScanner ,顺序为 1024

对于要增强的方法的增强顺序就是: 标准AOP增强 => 全局事物增强器 => 异步增强器。

同时对方法增强,那么方法有调用链如下:

名称顺序插入方式第一次第二次第三次
标准AOP2advised.add(interceptor)处理增强未被增强未被增强
全局事物增强器1advised.add(0, interceptor)已被增强处理增强未被增强
异步增强器0advised.add(0, interceptor)已被增强已被增强处理增强

最先由标准AOP增强Bean,bean有一个调用链, 然后由全局事物增强器增强曾经被增强的Bean, 这个添加一定会把调用链插入到头部, 最后由异步增强器增强。

所以Seata定义的BeanPostProcessor 不能是 Ordered.HIGHEST_PRECEDENCE 也不能是 Ordered.LOWEST_PRECEDENCE, 避免顺序混淆

方法拦截器

方法拦截器会判断当前方法是加了全局事务还是全局锁, 选择其中一个执行

// 获取全局事物注解
final GlobalTransactional globalTransactionalAnnotation =
    getAnnotation(method, targetClass, GlobalTransactional.class);
    // 获取全局锁注解
final GlobalLock globalLockAnnotation = getAnnotation(method,
		 targetClass, GlobalLock.class);
// 判断是否配置了禁用分布式事务,或者降级检查
boolean localDisable = disable 
		|| (degradeCheck && degradeNum >= degradeCheckAllowTimes);
if (!localDisable) {
	// 如果有全局事物
    if (globalTransactionalAnnotation != null) {
        return handleGlobalTransaction(methodInvocation, 
        		globalTransactionalAnnotation);
    }
    // 如果有全局锁 
    else if (globalLockAnnotation != null) {
        return handleGlobalLock(methodInvocation, globalLockAnnotation);
    }
}

全局事物拦截器:

在经过全局事物的方法拦截器时, 首先判断是否禁用全局事物, 然后判断是否开启降级检查。如果开启了降级检查,seata会开启一个调度,默认每2秒执行一次,默认情况下达到10次失败,就会进行服务降级, 不会开启分布式事务。

  1. 处理事务的传播机制
  2. 处理全局锁的挂起
  3. 向协调者发送全局事物开始请求,或的XID,绑定到当前线程, 执行RPC调用时,会将XID透传
  4. 执行业务逻辑
  5. 如果成功就向协调者发起事物提交请求, 失败就向协调者发起全局事物回滚请求
  6. 处理全局锁的释放与恢复, 处理全局事物的恢复

全局锁拦截器:

全局锁在Seata中用的范围很小, 在AT模式中,业务操作执行完成,并且回滚日志也插入完成,在提交之前需要获取全局锁。(只有加入了@GlobalLock才在提交之前获取全局锁, 不加的话,数据库连接执行提交之前是不需要全局锁的)

  1. 修改全局锁配置
  2. 执行业务方法, (数据库连接提交的时候, 分布式事务提交失败后重试的时候,会用到全局锁)
  3. 恢复全局锁配置
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值