由于我们的客户中心发送线索、整车订单服务配车、整车财务服务财务信息推送中都使用的全局事务是基于@GlobalTransactional、@TwoPhaseBusinessAction,因此有必要了解这两个注解中是如何在分布式事务中发挥作用的,也即TCC的实现。虽然AT模式比TCC模式要好。
一、TCC实现模式的分类
资源预留模式 补偿模式
1)资源预留模式
在第一阶段预先留下资源,从而来保证第二阶段的执行。
比如在财务中,第一阶段将要支付的金额进行冻结,也即中间状态。在第二阶段如果是提交,则进行金额扣减,从而账号金额发生变化。如果是回滚,则将冻结进行取消,从而保证金额不会发生变化。
此时:
try阶段:完成业务检查,和预留好资源
confirm阶段:基于try执行真正的业务逻辑
cancel阶段:释放预留的资源
2)补偿模式
在实际业务中,补偿模式要用得多一些。比如下订单、减库存的时候,就会用到。
比如:下订单的时候,首先进行一个插入操作,减库存的第一阶段减少库存,并记录库存减少的数量。
第二阶段,如果是全局提交,则不用做什么,只需要将第一阶段记录的信息进行删除即可。如果是全局回滚,则进行补偿,将扣减的库存还回来即可。
此时:
try阶段: 执行真正的业务逻辑
confirm阶段: 将额外的记录信息删除即可
cancel阶段: 根据额外的记录信息对try发生失败进行补偿
二、Seata中,业务系统注解的增强
和AT模式一样,TCC模式的事务的发起方都是需要添加@GlobalTransactional这个注解的。因此我们可以看着这里有两个拦截器值得我们去关注,TccActionInterceptor与GlobalTransactionalInterceptor全局事务拦截器。因为它们是进行业务系统分布式事务增强的源头。
TCC模式的实现可以根据注解TwoPhaseBusinessAction找到对应的拦截器TccActionInterceptor。从而进一步找到
TccActionInterceptor implements MethodInterceptor, ConfigurationChangeListener, Ordered
在往上看,可以看到其是基于AOP实现的增强:
GlobalTransactionScanner extends AbstractAutoProxyCreator implements ConfigurationChangeListener, InitializingBean, ApplicationContextAware, DisposableBean {
因为其实现了AbstractAutoProxyCreator抽象自动代理创建器和InitializingBean、ApplicationContextAware。因此我们可以看到它的重要方法:wrapIfNecessary。这个如果看过Spring的AOP源码的话,就会知道这个方法是进行AOP增强的必经之路。
if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) { // init tcc fence clean task if enable useTccFence TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext); //TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName)); ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, (ConfigurationChangeListener)interceptor); }else { //接口和实现都能够找到拦截器,进行全局事务拦截 Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean); Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean); if (!existsAnnotation(serviceInterface) && !existsAnnotation(interfacesIfJdk)) { return bean; } if (globalTransactionalInterceptor == null) { globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook); ConfigurationCache.addConfigListener( ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, (ConfigurationChangeListener)globalTransactionalInterceptor); } interceptor = globalTransactionalInterceptor; } //进行aop的增强 if (!AopUtils.isAopProxy(bean)) { bean = super.wrapIfNecessary(bean, beanName, cacheKey); } else { AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null)); int pos; for (Advisor avr : advisor) { // Find the position based on the advisor's order, and add to advisors by pos pos = findAddSeataAdvisorPosition(advised, avr); advised.addAdvisor(pos, avr); } } PROXYED_SET.add(beanName); return bean; }
是否是TCC自动代理,如果是则初始化TCC清理Task,同时创建TCCActionInterceptor对象,添加到配置缓存中,以便可以扫描到带有TwoPhaseBusinessAction的class。
同时我们注意到其实TCC和AT模式一样,事务的发起方都是需要添加@GlobalTransactional这个注解的。因此还有一个拦截器GlobalTransactionalInterceptor全局事务拦截器。
下一篇我们来看看,TccActionInterceptor和GlobalTransactionalInterceptor都做了哪些事情。因为它们必须要和两个注解建立联系,才能发挥作用。