从零开始写一个分布式事务框架(四)-事务管理器
Transaction Manager:事务管理器。控制分布式事务的边界,负责开启一个分布式事务,并最终发起分布式事务提交或回滚的决议。
事务管理器定义的接口如下:
|
|
开启分布式事务
首先我们看下start
方法:
|
|
首先,分布式事务需要在一个本地事务的环境中执行,才能将主事务记录的更新和业务本地事务的更新放在同一个本地事务环境中一起提交或回滚,因为我们是根据本地事务是否提交完成来做为整笔分布式事务是否完成的标志,所以先校验当前是否已经在本地事务环境中;然后生成事务ID,并生成Activity主事务记录,Activity记录中包含了事务ID,事务名称,事务的超时时间,补偿字段等信息;然后使用ActivityRepository.insert
插入Activity记录,这里需要注意,我们在insert接口上增加了@Transactional(propagation = Propagation.REQUIRES_NEW)
注解,使用到了Propagation.REQUIRES_NEW
事务传播属性,该事务传播属性的作用是,如果当前在一个事务中,则挂起当前事务,创建一个新的事务来执行insert
,这保证无论执行中出现任何异常,分布式事务记录一定已经写库完成,后续就可以根据事务记录的状态来做补偿等操作了;然后执行ActivityRepository.update
将主事务状态从INIT
更新为COMMIT
状态,update
使用了Propagation.REQUIRED
事务传播属性,是为了将该更新操作和本地业务事务放到一个本地事务中,这样在补偿时,根据Activity记录的状态是否是COMMIT
状态,就可以知道分布式事务应该提交还是回滚;最后使用TransactionSynchronizationManager
注册一个事务同步器,事务同步器提供了本地事务执行过程中的一系列扩展点,可以在本地事务提交前后进行拦截,这里我们使用到了beforeCommit
和afterCompletion
扩展点,用来进行校验操作和分布式事务的二阶段提交操作。
|
|
beforeCommit
触发时机是在本地事务提交前,在这里我们对分布式事务的超时时间进行校验,超时则禁止本地事务提交,进而触发分布式事务的回滚;
afterCompletion
触发时机是在本地事务提交或回滚完成后触发,在这里我们根据本地事务提交的状态是提交还是回滚,就可以触发TransactionManager
的二阶段commit
或rollback
方法了,同时需要在执行完成后在这里进行事务上下文的资源清理工作。
分支事务注册
服务之间的调用一般通过RPC或HTTP框架进行通信,远程调用发起的过程中我们一般可以通过切面SpringAOP的方式,拦截远程调用,注册分支事务。这里我们使用Dubbo作为服务之间的通信框架,Dubbo在使用xml的配置方式时,通过命名空间提供的解析器,会将分支事务的服务接口创建一个BeanDefinition
,这样的bean就会执行Spring的扩展点,比如通过BeanPostProcessor
相关扩展点比如AbstractAutoProxyCreator
,对bean进行AOP增强。然而如果不使用dubboxml配置而是直接使用@org.apache.dubbo.config.annotation.Reference
注解进行bean的引入时,则不会创建BeanDefinition
,而是直接在ReferenceAnnotationBeanPostProcessor
中通过ReferenceBeanBuilder
直接创建一个bean然后放到Springbean工厂里,这种情况下创建的bean不会执行Springbean创建过程中的各个扩展点,也就无法进行AOP增强通过AOP的方式在切面进行分支事务注册。所以我们没有采用SpringAOP的方式,而是使用dubbo的Filter扩展点。进行分支事务的注册。
|
|
|
|
META-INF/dubbo/com.alibaba.dubbo.rpc.Filter文件:
|
|
首先判断当前是否在分布式事务环境中;然后判断当前调用的方法是否有@TwoPhaseCommit
注解,如果有该注解则说明调用的方法是一个分支事务的一阶段方法,然后创建一个Action分支事务记录,Action记录中包含主事务的id,分支名称,分支事务的状态,参数等分支事务基本信息;然后执行ResourceManager.registerAction
注册一个分支事务。
提交或回滚分布式事务
|
|
commit
方法中,我们先执行ResourceManager.commitAction
方法将之前通过ResourceManager.registerAction
注册的所有分支事务提交,然后将主事务记录的状态从COMMIT
更新为COMMIT_FINISH
。
|
|
rollback
方法中,我们先执行ResourceManager.rollbackAction
方法将所有分支事务回滚,然后将主事务记录的状态从COMMIT
更新为COMMIT_FINISH
。
至此,整个TransactionManager
事务管理器的相关逻辑就都完成了,接下来我们看下本文中多次提到的ResourceManager
资源管理器如何实现。