分布式事务:TCC

开源源码地址:https://github.com/changmingxie/tcc-transaction.git

项目demo地址:https://github.com/liuchaoOvO/tcc-transaction-demo.git

源码修改成springboot 源码地址:https://github.com/liuchaoOvO/tcc-transaction.git

主要涉及如下三个 Maven 项目:

  • tcc-transaction-core :tcc-transaction 底层实现。

  • tcc-transaction-api :tcc-transaction 使用 API。

  • tcc-transaction-spring :tcc-transaction Spring 支持。

目录

TCC事务简介

架构模型图

与 2PC协议 比较

源码

添加tcc-transaction的使用方法


TCC事务简介

为了解决在事务运行过程中,大颗粒度资源锁定的问题,提出一种新的事务模型,它是基于业务层面的事务定义。锁粒度完全由业务自己控制。它本质是一种补偿的思路。它把事务运行过程分成 Try、Confirm / Cancel 两个阶段。在每个阶段的逻辑由业务代码控制。这样就事务的锁粒度可以完全自由控制。业务可以在牺牲隔离性的情况下,获取更高的性能。本质上,TCC 通过多个参与者的 try / confirm / cancel 方法,实现事务的最终一致性

架构模型图

在这里插入图片描述

  • Try 阶段

    • 完成所有业务检查( 一致性 )

    • 预留必须业务资源( 准隔离性 )

    • Try :尝试执行业务

  • Confirm / Cancel 阶段:

    • 释放 Try 阶段预留的业务资源

    • Cancel 操作满足幂等性(先getXXXID()得到对应Try 尝试保存的数据,再补偿取消)

    • 真正执行业务

    • 不做任务业务检查

    • Confirm 操作满足幂等性

    • Confirm :确认执行业务

    • Cancel :取消执行业务

    • Confirm 与 Cancel 互斥

与 2PC协议 比较

  • 没有单独的准备( Prepare )阶段,Try 操作兼备自愿操作与准备能力

  • Try 操作可以灵活选择业务资源的锁定粒度

  • 较高开发成本

 在这里插入图片描述

 

TCC柔性补偿性事务:尝试/ 确定/ 取消  三个接口都要我们自己去实现

在 TCC 里,一个业务活动可以有多个事务,每个业务操作归属于不同的事务,即一个事务可以包含多个业务操作。TCC-Transaction 将每个业务操作抽象成事务参与者,每个事务可以包含多个参与者

TCC-Transaction 有两个拦截器,通过对 @Compensable AOP 切面( 参与者 try 方法 )进行拦截,透明化对参与者 confirm / cancel 方法调用,从而实现 TCC 。

两种拦截器:

可补偿事务拦截器:CompensableTransactionInterceptor

资源协调者拦截器:ResourceCoordinatorInterceptor

第一个拦截器,可补偿事务拦截器,实现如下功能:

  • 在 Try 阶段,对事务的发起、传播。

  • 在 Confirm / Cancel 阶段,对事务提交或回滚。

  • 为什么会有对事务的传播呢?  在远程调用服务的参与者时,会通过"参数"( 需要序列化 )的形式传递事务给远程参与者。

第二个拦截器,资源协调者拦截器,实现如下功能:

  • 在 Try 阶段,添加参与者到事务中。当事务上下文不存在时,进行创建。

源码

1、参与者对象 Participant :

public class Participant implements Serializable {

   /**
     * 事务编号
     */
    private TransactionXid xid;
    /**
     * 确认执行业务方法调用上下文
     */
    private InvocationContext confirmInvocationContext;
    /**
     * 取消执行业务方法
     */
    private InvocationContext cancelInvocationContext;
    /**
     * 执行器
     */
    private Terminator terminator = new Terminator();
    /**
     * 事务上下文编辑
     */
    Class<? extends TransactionContextEditor> transactionContextEditorClass;

    /**
     * 提交事务
     */
    public void commit() {
        terminator.invoke(new TransactionContext(xid, TransactionStatus.CONFIRMING.getId()), confirmInvocationContext, transactionContextEditorClass);
    }

    /**
     * 回滚事务
     */
    public void rollback() {
        terminator.invoke(new TransactionContext(xid, TransactionStatus.CANCELLING.getId()), cancelInvocationContext, transactionContextEditorClass);
    }

    ...
}

2、InvocationContext。

执行方法调用上下文,记录类、方法名、参数类型数组、参数数组。通过这些属性,可以执行提交 / 回滚事务。在 org.mengyun.tcctransaction.Terminator 会看到具体的代码实现。

3、Terminator 执行器

public class Terminator implements Serializable {
private static final long serialVersionUID = -164958655471605778L;

public Object invoke(TransactionContext transactionContext, InvocationContext invocationContext, Class&lt;? extends TransactionContextEditor&gt; transactionContextEditorClass) {
    if (StringUtils.isNotEmpty(invocationContext.getMethodName())) {
        try {
            // 获得 参与者对象
            Object target = FactoryBuilder.factoryOf(invocationContext.getTargetClass()).getInstance();
            // 获得 方法
            Method method = target.getClass().getMethod(invocationContext.getMethodName(), invocationContext.getParameterTypes());
            // 设置 事务上下文 到方法参数
            FactoryBuilder.factoryOf(transactionContextEditorClass).getInstance().set(transactionContext, target, method, invocationContext.getArgs());
            // 执行方法
            return method.invoke(target, invocationContext.getArgs());
        } catch (Exception e) {
            throw new SystemException(e);
        }
    }
    return null;
}

4、事务管理器 TransactionManager

事务管理器不关心接口是怎么实现的,只会根据情况去调用。1、启动事务。2、调用尝试的接口,3、提交或者失败调用取消接口。

TransactionManager

发起根事务

transactionManager 提供 begin() 方法,发起根事务。该方法在调用方法类型为 MethodType.ROOT 并且 事务处于 Try 阶段被调用。

TCC-Transaction 支持多个的事务独立存在,后创建的事务先提交(原因:ThreadLocal<Deque<Transaction>>),类似 Spring 的org.springframework.transaction.annotation.Propagation.REQUIRES_NEW 

传播发起分支事务

transactionManager 调用 #propagationNewBegin(...) 方法,传播发起分支事务。该方法在调用方法类型为 MethodType.PROVIDER 并且 事务处于 Try 阶段被调用。

传播获取分支事务

transactionManager 调用 #propagationExistBegin(...) 方法,传播发起分支事务。该方法在调用方法类型为 MethodType.PROVIDER 并且 事务处于 Confirm / Cancel 阶段被调用。

提交事务

transactionManager 调用 #commit(...) 方法,提交事务。该方法在事务处于 Confirm / Cancel 阶段被调用。

回滚事务

transactionManager 调用 #rollback(...) 方法,取消事务,和 #commit() 方法基本类似。该方法在事务处于 Confirm / Cancel 阶段被调用。

添加参与者到事务

调用 #enlistParticipant(...) 方法,添加参与者到事务。该方法在事务处于 Try 阶段被调用。

5、事务拦截器方法

package org.mengyun.tcctransaction.interceptor;
public class CompensableTransactionInterceptor {

    private TransactionManager transactionManager;

    private Set<Class<? extends Exception>> delayCancelExceptions;

    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }	
    public void setDelayCancelExceptions(Set<Class<? extends Exception>> delayCancelExceptions) {
        this.delayCancelExceptions = delayCancelExceptions;
    }

	//事务拦截器通过AOP环绕到加了注解@Compensable的方法,环绕执行下面的方法
    public Object interceptCompensableMethod(ProceedingJoinPoint pjp) throws Throwable {
		//获取到加了注解的方法本身
        Method method = CompensableMethodUtils.getCompensableMethod(pjp);
		//获取到注解@Compensable自己本身 --> 获取注解的属性:取消的方法,确认的方法
        Compensable compensable = method.getAnnotation(Compensable.class);
        Propagation propagation = compensable.propagation();
        //获取事务上下文,RPC隐式参数中包含这个上下文
        TransactionContext transactionContext = FactoryBuilder.factoryOf(compensable.transactionContextEditor()).getInstance().get(pjp.getTarget(), method, pjp.getArgs());

        boolean asyncConfirm = compensable.asyncConfirm();

        boolean asyncCancel = compensable.asyncCancel();
	    //判断是否有存在的事务队列
        boolean isTransactionActive = transactionManager.isTransactionActive();
		
        if (!TransactionUtils.isLegalTransactionContext(isTransactionActive, propagation, transactionContext)) {
            throw new SystemException("no active compensable transaction while propagation is mandatory for method " + method.getName());
        }

        MethodType methodType = CompensableMethodUtils.calculateMethodType(propagation, isTransactionActive, transactionContext);
		/*
		*这个方法判断:(!isTransactionActive && transactionContext == null)是否已经有事务 
        了,事务上下文是否为空
		都满足说明式主事务,否则是分支事务
		*/
        switch (methodType) {
            case ROOT:
                return rootMethodProceed(pjp, asyncConfirm, asyncCancel);
            case PROVIDER:
                return providerMethodProceed(pjp, transactionContext, asyncConfirm, asyncCancel);
            default:
                return pjp.proceed();
        }
    }

	/**这里是主事务的操作
	开启事务
	注册,持久化事务
	然后判断是cancel,confirm
	清除事务
	*/
    private Object rootMethodProceed(ProceedingJoinPoint pjp, boolean asyncConfirm, boolean asyncCancel) throws Throwable {

        Object returnValue = null;

        Transaction transaction = null;

        try {
			事务为空那么就开启一个全新的事务
            transaction = transactionManager.begin();

            try {
            //继续后续操作
                returnValue = pjp.proceed();
            } catch (Throwable tryingException) {

                if (!isDelayCancelException(tryingException)) {
                   
                    logger.warn(String.format("compensable transaction trying failed. transaction content:%s", JSON.toJSONString(transaction)), tryingException);

                    transactionManager.rollback(asyncCancel);
                }

                throw tryingException;
            }

            transactionManager.commit(asyncConfirm);

        } finally {
            transactionManager.cleanAfterCompletion(transaction);
        }

        return returnValue;
    }

    private Object providerMethodProceed(ProceedingJoinPoint pjp, TransactionContext transactionContext, boolean asyncConfirm, boolean asyncCancel) throws Throwable {

        Transaction transaction = null;
        try {

            switch (TransactionStatus.valueOf(transactionContext.getStatus())) {
                case TRYING:
                    transaction = transactionManager.propagationNewBegin(transactionContext);
                    return pjp.proceed();
                case CONFIRMING:
                    try {
                        transaction = transactionManager.propagationExistBegin(transactionContext);
                        transactionManager.commit(asyncConfirm);
                    } catch (NoExistedTransactionException excepton) {
                        //the transaction has been commit,ignore it.
                    }
                    break;
                case CANCELLING:

                    try {
                        transaction = transactionManager.propagationExistBegin(transactionContext);
                        transactionManager.rollback(asyncCancel);
                    } catch (NoExistedTransactionException exception) {
                        //the transaction has been rollback,ignore it.
                    }
                    break;
            }

        } finally {
            transactionManager.cleanAfterCompletion(transaction);
        }

        Method method = ((MethodSignature) (pjp.getSignature())).getMethod();

        return ReflectionUtils.getNullValue(method.getReturnType());
    }

    private boolean isDelayCancelException(Throwable throwable) {

        if (delayCancelExceptions != null) {
            for (Class delayCancelException : delayCancelExceptions) {

                Throwable rootCause = ExceptionUtils.getRootCause(throwable);

                if (delayCancelException.isAssignableFrom(throwable.getClass())
                        || (rootCause != null && delayCancelException.isAssignableFrom(rootCause.getClass()))) {
                    return true;
                }
            }
        }

        return false;
    }

}

添加tcc-transaction的使用方法

1、需要提供分布式事务支持的接口上添加@Compensable

2、在对应的接口实现上添加@Compensable

3、在接口实现上添加confirmMethod、cancelMethod、transactionContextEditor

4、实现对应的confirmMethod、cancelMethod   注意:confirm方法和cancel方法必须与try方法在同一个类中.。(根据类的反射找到方法invoke)

注意:

1、分布式事务里,不要轻易在业务层捕获所有异常,抛出异常才能被TCC感知到,然后回调。只捕获必要捕获的异常。

2、使用TCC-Transaction时,confirm和cancel的幂等性需要自己编写代码支持

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章