Tx-lcn 源码解析:基于lcn模式下的正常流程源码(3)

 

一、代理Connect的获取

TX-LCN中的LCN模式是通过代理数据库连接,进而对事务进行控制的。通过静态代理的方式包装的原本来的Connection,并设置为手动提交,根据事务状态控制提交与回滚。

请记住:state为事务状态,state=1表示分布式事务成功,本地连接提交,其它为失败进行本地连接回滚。

代理连接代码如下:



package com.codingapi.txlcn.tc.core.transaction.lcn.resource;

import com.codingapi.txlcn.txmsg.dto.RpcResponseState;
import lombok.extern.slf4j.Slf4j;

import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * @author CoderTnT
 */
@Slf4j
public class LcnConnectionProxy implements Connection {

    private Connection connection;

    public LcnConnectionProxy(Connection connection) {
        this.connection = connection;
    }

    /**
     * notify connection
     *
     * @param state transactionState
     * @return RpcResponseState RpcResponseState
     */
    public RpcResponseState notify(int state) {
        try {
            if (state == 1) {
                log.debug("commit transaction type[lcn] proxy connection:{}.", this);
                connection.commit();
            } else {
                log.debug("rollback transaction type[lcn] proxy connection:{}.", this);
                connection.rollback();
            }
            connection.close();
            log.debug("transaction type[lcn] proxy connection:{} closed.", this);
            return RpcResponseState.success;
        } catch (Exception e) {
            log.error(e.getLocalizedMessage(), e);
            return RpcResponseState.fail;
        }
    }
    
    
    // autoCommit设置false,表示 连接关闭自动提交,采用手动提交模式
    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        connection.setAutoCommit(false);
    }

    @Override
    public void commit() throws SQLException {
        connection.commit();
    }

    @Override
    public void rollback() throws SQLException {
        connection.rollback();
    }

    @Override
    public void close() throws SQLException {
        connection.close();
    }


    @Override
    public boolean getAutoCommit() throws SQLException {
        return connection.getAutoCommit();
    }
    /*
    * 忽略其他无关代码。。。
    */

}

 LcnConnectionProxy代理连接的获取是通过AOP实现的

@Aspect
@Component
@Slf4j
public class DataSourceAspect implements Ordered {

    private final TxClientConfig txClientConfig;

    private final DTXResourceWeaver dtxResourceWeaver;

    public DataSourceAspect(TxClientConfig txClientConfig, DTXResourceWeaver dtxResourceWeaver) {
        this.txClientConfig = txClientConfig;
        this.dtxResourceWeaver = dtxResourceWeaver;
    }

    
    //环绕通知获取代理connection连接
    @Around("execution(* javax.sql.DataSource.getConnection(..))")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        return dtxResourceWeaver.getConnection(() -> (Connection) point.proceed());
    }


    @Override
    public int getOrder() {
        return txClientConfig.getResourceOrder();
    }


}
@Component
@Slf4j
public class DTXResourceWeaver {

    private final TxLcnBeanHelper txLcnBeanHelper;

    public DTXResourceWeaver(TxLcnBeanHelper txLcnBeanHelper) {
        this.txLcnBeanHelper = txLcnBeanHelper;
    }

    public Object getConnection(ConnectionCallback connectionCallback) throws Throwable {
        //获取事务本地上下文 dtxLocalContext
        DTXLocalContext dtxLocalContext = DTXLocalContext.cur();
        if (Objects.nonNull(dtxLocalContext) && dtxLocalContext.isProxy()) {
            //获取事务本地上下文的事务类型  dtxLocalContext的事务类型
            String transactionType = dtxLocalContext.getTransactionType();
            //根据事务类型获取事务代理器:LcnTransactionResourceProxy或TccTransactionResourceProxy或TxcTransactionResourceProxy
            TransactionResourceProxy resourceProxy = txLcnBeanHelper.loadTransactionResourceProxy(transactionType);
            //由事务代理器根据connectionCallback Connection提供者 构造生成代理连接connection对象
            Connection connection = resourceProxy.proxyConnection(connectionCallback);
            log.debug("proxy a sql connection: {}.", connection);
            return connection;
        }
        return connectionCallback.call();
    }
}

二、整体正常执行的代码流程

实现分布式事务需要在特定执行的service方法上增加一个注解@LcnTransaction,这也是一个入口,整个流程的开始就在这里。

TransactionAspect类是一个切面类,对@LcnTransaction注解设置了环绕通知,在碰到注解@LcnTransaction开始了整个流程。

2.1 分布式事务执行方法的环绕

@Aspect    //切面类注解
@Component
@Slf4j
public class TransactionAspect implements Ordered {

    private final TxClientConfig txClientConfig;

    private final DTXLogicWeaver dtxLogicWeaver;

    public TransactionAspect(TxClientConfig txClientConfig, DTXLogicWeaver dtxLogicWeaver) {
        this.txClientConfig = txClientConfig;
        this.dtxLogicWeaver = dtxLogicWeaver;
    }

    /**
     * DTC Aspect Common type() can lcn,tcc,txc and custom DTX type
     */
    @Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.TxTransaction)")
    public void txTransactionPointcut() {
    }

    /**
     * DTC Aspect (Type of LCN)
     */
    @Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.LcnTransaction)")
    public void lcnTransactionPointcut() {
    }

    /**
     * DTC Aspect (Type of TXC)
     */
    @Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.TxcTransaction)")
    public void txcTransactionPointcut() {
    }

    /**
     * DTC Aspect (Type of TCC)
     */
    @Pointcut("@annotation(com.codingapi.txlcn.tc.annotation.TccTransaction)")
    public void tccTransactionPointcut() {
    }


    @Around("txTransactionPointcut()")
    public Object transactionRunning(ProceedingJoinPoint point) throws Throwable {
        //见2.1.1
        DTXInfo dtxInfo = DTXInfo.getFromCache(point);
        TxTransaction txTransaction = dtxInfo.getBusinessMethod().getAnnotation(TxTransaction.class);
        dtxInfo.setTransactionType(txTransaction.type());
        dtxInfo.setTransactionPropagation(txTransaction.propagation());
        return dtxLogicWeaver.runTransaction(dtxInfo, point::proceed);
    }

    @Around("lcnTransactionPointcut() && !txcTransactionPointcut()" +
            "&& !tccTransactionPointcut() && !txTransactionPointcut()")
    public Object runWithLcnTransaction(ProceedingJoinPoint point) throws Throwable {
        //根据ProceedingJoinPoint  从缓存中获取事务信息dtxInfo
        DTXInfo dtxInfo = DTXInfo.getFromCache(point);
        //获取注解LcnTransaction,因为注解LcnTransaction上配置不同的传播行为propagation,如REQUIRED,SUPPORTS等
        LcnTransaction lcnTransaction = dtxInfo.getBusinessMethod().getAnnotation(LcnTransaction.class);
        //设置dtxInfo的事务类型为LCN
        dtxInfo.setTransactionType(Transactions.LCN);
        //设置事务传播行为,发起方是REQUIRED,参与方式是SUPPORTS
        dtxInfo.setTransactionPropagation(lcnTransaction.propagation());
        //织入后 开始执行事务
        //见2.1.2
        return dtxLogicWeaver.runTransaction(dtxInfo, point::proceed);
    }
   /*
   *  忽略其他代码。。。
   */
}

2.1.1 在注解的开始从缓存中获取了事务信息dtxInfo

 public static DTXInfo getFromCache(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 根据切入点 获取方法签名信息signature
        String signature = proceedingJoinPoint.getSignature().toString();
        //根据方法签名生成事务单元ID
        String unitId = Transactions.unitId(signature);
        //根据事务单元ID获取事务信息dtxInfo
        DTXInfo dtxInfo = dtxInfoCache.get(unitId);
        if (Objects.isNull(dtxInfo)) {
            // 根据切入点 获取方法签名信息methodSignature
            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
            //获取要执行的方法,这是接口的方法并不是我们要的方法
            Method method = methodSignature.getMethod();
            //获取目标类
            Class targetClass = proceedingJoinPoint.getTarget().getClass();
            //获取真实的实现方法
            Method thisMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
            //构造dtxInfo 对象
            dtxInfo = new DTXInfo(thisMethod, proceedingJoinPoint.getArgs(), targetClass);
            //缓存dtxInfo 对象 键:事务单元ID,值:dtxInfo
            dtxInfoCache.put(unitId, dtxInfo);
        }
        dtxInfo.reanalyseMethodArgs(proceedingJoinPoint.getArgs());
        return dtxInfo;
    }

2.1.2 事务执行runTransaction方法

public Object runTransaction(DTXInfo dtxInfo, BusinessCallback business) throws Throwable {
        // 1、获取或新建一个线程变量DTXLocalContext
        if (Objects.isNull(DTXLocalContext.cur())) {
            DTXLocalContext.getOrNew();
        } else {
            return business.call();
        }

        log.debug("<---- TxLcn start ---->");
        DTXLocalContext dtxLocalContext = DTXLocalContext.getOrNew();
        TxContext txContext;
        // ---------- 保证每个模块在一个DTX下只会有一个TxContext ---------- //
        //2、如果已经存在事务上下文则获取,否则构建事务上下文TxContext 
        if (globalContext.hasTxContext()) {
            // 有全局TC事务上下文的获取上下文
            txContext = globalContext.txContext();
            dtxLocalContext.setInGroup(true);
            log.debug("Unit[{}] used parent's TxContext[{}].", dtxInfo.getUnitId(), txContext.getGroupId());
        } else {
            // 没有的开启本地事务上下文
            // 见2.1.4
            txContext = globalContext.startTx();
        }

        // 本地事务调用
        if (Objects.nonNull(dtxLocalContext.getGroupId())) {
            dtxLocalContext.setDestroy(false);
        }
        //3、为DTXLocalContext设置参数
        dtxLocalContext.setUnitId(dtxInfo.getUnitId());
        dtxLocalContext.setGroupId(txContext.getGroupId());
        dtxLocalContext.setTransactionType(dtxInfo.getTransactionType());

        // 事务参数
        //4、构造TxTransactionInfo 并设置参数
        TxTransactionInfo info = new TxTransactionInfo();
        info.setBusinessCallback(business);
        info.setGroupId(txContext.getGroupId());
        info.setUnitId(dtxInfo.getUnitId());
        info.setPointMethod(dtxInfo.getBusinessMethod());
        info.setPropagation(dtxInfo.getTransactionPropagation());
        info.setTransactionInfo(dtxInfo.getTransactionInfo());
        info.setTransactionType(dtxInfo.getTransactionType());
        info.setTransactionStart(txContext.isDtxStart());

        //LCN事务处理器
        try {
            //执行5、执行事务 事务业务执行
            return transactionServiceExecutor.transactionRunning(info);
        } 
        //6、finally做一些通知与销毁操作。
        //见2.1.5
        finally {
            // 线程执行业务完毕清理本地数据
            if (dtxLocalContext.isDestroy()) {
                // 通知事务执行完毕
                synchronized (txContext.getLock()) {
                    txContext.getLock().notifyAll();
                }

                // TxContext生命周期是和事务组一样(不与具体模块相关的)
                if (!dtxLocalContext.isInGroup()) {
                    globalContext.destroyTx();
                }

                DTXLocalContext.makeNeverAppeared();
                TracingContext.tracing().destroy();
            }
            log.debug("<---- TxLcn end ---->");
        }
    }

2.1.3 主要说下2.1.2代码注释中的第2点什么情况下要获取已有的TxContext ,什么时候构造新的

如果一个调用链是这样的A->B->C 三个都是不同的模块则会为每个模块都构造新的TxContext,保证一个模块一个。

如果三个是两个模块,如A是一个模块,B和C是一个模块根据调用链的情况肯定是C的用B的TxContext。

2.1.4 构建新的TxContext

public TxContext startTx() {
        TxContext txContext = new TxContext();
        // 事务发起方判断
        txContext.setDtxStart(!TracingContext.tracing().hasGroup());
        // 是否为事务发起方
        if (txContext.isDtxStart()) {
            //是的话开始 初始化事务组操作,主要是构造事务组groupId,并放入以groupId和appMap为键的Map 到tracingContext对象缓存中
            TracingContext.tracing().beginTransactionGroup();
        }
        //设置txContext事务组groupId
        txContext.setGroupId(TracingContext.tracing().groupId());
        String txContextKey = txContext.getGroupId() + ".dtx";
        //key与txContext缓存起来
        attachmentCache.attach(txContextKey, txContext);
        log.debug("Start TxContext[{}]", txContext.getGroupId());
        return txContext;
    }

2.1.5  线程执行完的一些销毁动作 放到finally 块中

finally {
            // 线程执行业务完毕清理本地数据
            if (dtxLocalContext.isDestroy()) {
                // 通知事务执行完毕
                synchronized (txContext.getLock()) {
                    txContext.getLock().notifyAll();
                }

                // TxContext生命周期是和事务组一样(不与具体模块相关的)
                if (!dtxLocalContext.isInGroup()) {
                    globalContext.destroyTx();
                }

                DTXLocalContext.makeNeverAppeared();
                TracingContext.tracing().destroy();
            }
            log.debug("<---- TxLcn end ---->");
        }
@Override
    public void destroyTx() {
        if (!hasTxContext()) {
            throw new IllegalStateException("non TxContext.");
        }
        // 销毁缓存中的 以groupId.dtx 为键的事务组信息
        destroyTx(txContext().getGroupId());
    }

业务执行完会把attachmentCache的key移除掉,后面在参与方clean事务的时候才不会await。 

 @Override
    public void destroyTx(String groupId) {
        attachmentCache.remove(groupId + ".dtx");
        log.debug("Destroy TxContext[{}]", groupId);
    }

三、LCN分布式事务业务执行器 开始执行事务:transactionServiceExecutor.transactionRunning(info)

/**
     * 事务业务执行
     *
     * @param info info
     * @return Object
     * @throws Throwable Throwable
     */
    public Object transactionRunning(TxTransactionInfo info) throws Throwable {

        // 1. 获取事务类型
        String transactionType = info.getTransactionType();

        // 2. 获取事务传播状态
        DTXPropagationState propagationState = propagationResolver.resolvePropagationState(info);

        // 2.1 如果不参与分布式事务立即终止
        if (propagationState.isIgnored()) {
            return info.getBusinessCallback().call();
        }

        // 3. 获取本地分布式事务控制器   dtxLocalControl :事务发起方控制器是LcnStartingTransaction,事务参与方控制器是LcnRunningTransaction
        DTXLocalControl dtxLocalControl = txLcnBeanHelper.loadDTXLocalControl(transactionType, propagationState);

        // 4. 织入事务操作
        try {
            // 4.1 记录事务类型到事务上下文
            Set transactionTypeSet = globalContext.txContext(info.getGroupId()).getTransactionTypes();
            transactionTypeSet.add(transactionType);

            dtxLocalControl.preBusinessCode(info);

            // 4.2 业务执行前
            txLogger.txTrace(
                    info.getGroupId(), info.getUnitId(), "pre business code, unit type: {}", transactionType);

            // 4.3 执行业务
            Object result = dtxLocalControl.doBusinessCode(info);

            // 4.4 业务执行成功
            txLogger.txTrace(info.getGroupId(), info.getUnitId(), "business success");
            dtxLocalControl.onBusinessCodeSuccess(info, result);
            return result;
        } catch (TransactionException e) {
            txLogger.error(info.getGroupId(), info.getUnitId(), "before business code error");
            throw e;
        } catch (Throwable e) {
            // 4.5 业务执行失败
            txLogger.error(info.getGroupId(), info.getUnitId(), Transactions.TAG_TRANSACTION,
                    "business code error");
            dtxLocalControl.onBusinessCodeError(info, e);
            throw e;
        } finally {
            // 4.6 业务执行完毕
            dtxLocalControl.postBusinessCode(info);
        }
    }

3.1 事务控制器根据事务类型与传播状态执行的也不同。获取传播类型:发起方是create,参与方是join

 @Override
    public DTXPropagationState resolvePropagationState(TxTransactionInfo txTransactionInfo) throws TransactionException {

        // 本地已在DTX,根据事务传播,静默加入
        if (DTXLocalContext.cur().isInGroup()) {
            log.info("SILENT_JOIN group!");
            return DTXPropagationState.SILENT_JOIN;
        }

        // 发起方之前没有事务
        if (txTransactionInfo.isTransactionStart()) {
            // 根据事务传播,对于 SUPPORTS 不参与事务
            if (DTXPropagation.SUPPORTS.equals(txTransactionInfo.getPropagation())) {
                return DTXPropagationState.NON;
            }
            // 根据事务传播,创建事务
            return DTXPropagationState.CREATE;
        }

        // 已经存在DTX,根据事务传播,加入
        return DTXPropagationState.JOIN;
    }

3.2 事务发起方控制器是LcnStartingTransaction,事务参与方控制器是LcnRunningTransaction

Tx-lcn 源码解析:基于lcn模式下的正常流程源码(3)_第1张图片

3.3 这里分为四步执行业务前、执行业务,执行业务后、最后finally。

3.3.1 执行业务前preBusinessCode方法:dtxLocalControl.preBusinessCode(info);

1)发起方:LcnStartingTransaction

通过netty调用服务端创建事务组

设置DTXLocalContext的proxy为true。

@Override
    public void preBusinessCode(TxTransactionInfo info) throws TransactionException {
        // create DTX group
        transactionControlTemplate.createGroup(
                info.getGroupId(), info.getUnitId(), info.getTransactionInfo(), info.getTransactionType());

        // lcn type need connection proxy
        DTXLocalContext.makeProxy();
    }
 /**
     * Client创建事务组操作集合
     *
     * @param groupId         groupId
     * @param unitId          unitId
     * @param transactionInfo txTrace
     * @param transactionType transactionType
     * @throws TransactionException 创建group失败时抛出
     */
    public void createGroup(String groupId, String unitId, TransactionInfo transactionInfo, String transactionType)
            throws TransactionException {
        //创建事务组
        try {
            // 日志
            txLogger.txTrace(groupId, unitId,
                    "create group > transaction type: {}", transactionType);
            // 创建事务组消息
            reliableMessenger.createGroup(groupId);
            // 缓存发起方切面信息
            aspectLogger.trace(groupId, unitId, transactionInfo);
        } catch (RpcException e) {
            // 通讯异常
            dtxExceptionHandler.handleCreateGroupMessageException(groupId, e);
        } catch (LcnBusinessException e) {
            // 创建事务组业务失败
            dtxExceptionHandler.handleCreateGroupBusinessException(groupId, e.getCause());
        }
        txLogger.txTrace(groupId, unitId, "create group over");
    }
@Override
    public void createGroup(String groupId) throws RpcException, LcnBusinessException {
        // TxManager创建事务组
        MessageDto messageDto = request(MessageCreator.createGroup(groupId));
        if (!MessageUtils.statusOk(messageDto)) {
            throw new LcnBusinessException(messageDto.loadBean(Throwable.class));
        }
    }

lcn-manager服务端接收到创建事务组信息后会用CreateGroupExecuteService的execute方法进行处理

@Service("rpc_create-group")
public class CreateGroupExecuteService implements RpcExecuteService {

    private static final TxLogger txLogger = TxLogger.newLogger(CreateGroupExecuteService.class);

    private final TransactionManager transactionManager;

    @Autowired
    public CreateGroupExecuteService(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    @Override
    public Serializable execute(TransactionCmd transactionCmd) throws TxManagerException {
        try {
            transactionManager.begin(transactionCmd.getGroupId());
        } catch (TransactionException e) {
            throw new TxManagerException(e);
        }
        txLogger.txTrace(transactionCmd.getGroupId(), null, "created group");
        return null;
    }
}

    @Override
    public void begin(String groupId) throws TransactionException {
        try {
            dtxContextRegistry.create(groupId);
        } catch (TransactionException e) {
            throw new TransactionException(e);
        }
    }
@Override
    public DTXContext create(String groupId) throws TransactionException {
        try {
            fastStorage.initGroup(groupId);
        } catch (FastStorageException e) {
            // idempotent processing
            if (e.getCode() != FastStorageException.EX_CODE_REPEAT_GROUP) {
                throw new TransactionException(e);
            }
        }
        return get(groupId);
    }
 @Override
    public void initGroup(String groupId) {
        redisTemplate.opsForHash().put(REDIS_GROUP_PREFIX + groupId, "root", "");
        redisTemplate.expire(REDIS_GROUP_PREFIX + groupId, managerConfig.getDtxTime() + 10000, TimeUnit.MILLISECONDS);
    }

lcn-manager服务端会以hash结构在redis创建一个key。hashkey为“tm:group:”+groupId,key为root,value为空。超时时间为分布式事务超时时间dtxTime(默认值8秒)外加上10s 一共18秒。

2)参与方 :LcnRunningTransaction

@Override
    public void preBusinessCode(TxTransactionInfo info) {
        // lcn type need connection proxy
        DTXLocalContext.makeProxy();
    }

3.3.2 执行业务方法doBusinessCode :dtxLocalControl.doBusinessCode(info);

对于执行业务方法,就是执行我们的方法的内容。主要是调用其他服务,而其他服务又会有@LcnTransaction注解,又会走同样的流程,只是处理类不一样了,这里都会写出不同处理类的处理方式。

/**
     * 执行业务代码
     *
     * @param info info
     * @return  Object Object
     * @throws Throwable Throwable
     */
    default Object doBusinessCode(TxTransactionInfo info) throws Throwable {
        // 调用执行服务
        return info.getBusinessCallback().call();
    }

3.3.3 业务执行成功方法onBusinessCodeSuccess:dtxLocalControl.onBusinessCodeSuccess(info, result);

1)发起方:LcnStartingTransaction

设置系统分布式事务状态为1,代表标识成功

 @Override
    public void onBusinessCodeSuccess(TxTransactionInfo info, Object result) {
        DTXLocalContext.cur().setSysTransactionState(1);
    }

2)参与方:LcnRunningTransaction

加入到事务组

@Override
    public void onBusinessCodeSuccess(TxTransactionInfo info, Object result) throws TransactionException {
        log.debug("join group: [GroupId: {},Method: {}]" , info.getGroupId(),
                info.getTransactionInfo().getMethodStr());
        
        // join DTX group
        transactionControlTemplate.joinGroup(info.getGroupId(), info.getUnitId(), info.getTransactionType(),
                info.getTransactionInfo());
    }
 /**
     * Client加入事务组操作集合
     *
     * @param groupId         groupId
     * @param unitId          unitId
     * @param transactionType transactionType
     * @param transactionInfo txTrace
     * @throws TransactionException 加入事务组失败时抛出
     */
    public void joinGroup(String groupId, String unitId, String transactionType, TransactionInfo transactionInfo)
            throws TransactionException {
        try {
            txLogger.txTrace(groupId, unitId, "join group > transaction type: {}", transactionType);

            reliableMessenger.joinGroup(groupId, unitId, transactionType, DTXLocalContext.transactionState(globalContext.dtxState(groupId)));

            txLogger.txTrace(groupId, unitId, "join group message over.");

            // 异步检测
            dtxChecking.startDelayCheckingAsync(groupId, unitId, transactionType);

            // 缓存参与方切面信息
            aspectLogger.trace(groupId, unitId, transactionInfo);
        } catch (RpcException e) {
            dtxExceptionHandler.handleJoinGroupMessageException(Arrays.asList(groupId, unitId, transactionType), e);
        } catch (LcnBusinessException e) {
            dtxExceptionHandler.handleJoinGroupBusinessException(Arrays.asList(groupId, unitId, transactionType), e);
        }
        txLogger.txTrace(groupId, unitId, "join group logic over");
    }
@Override
    public void joinGroup(String groupId, String unitId, String unitType, int transactionState) throws RpcException, LcnBusinessException {
        JoinGroupParams joinGroupParams = new JoinGroupParams();
        joinGroupParams.setGroupId(groupId);
        joinGroupParams.setUnitId(unitId);
        joinGroupParams.setUnitType(unitType);
        joinGroupParams.setTransactionState(transactionState);
        MessageDto messageDto = request(MessageCreator.joinGroup(joinGroupParams));
        if (!MessageUtils.statusOk(messageDto)) {
            throw new LcnBusinessException(messageDto.loadBean(Throwable.class));
        }
    }

lcn-client客户端会调用lcn-manager服务端去把当前操作加入到事务组中。服务端收到后会调用JoinGroupExecuteService类的execute进行处理。

@Service("rpc_join-group")
public class JoinGroupExecuteService implements RpcExecuteService {

    private static final TxLogger txLogger = TxLogger.newLogger(JoinGroupExecuteService.class);

    private final TransactionManager transactionManager;

    private final DTXContextRegistry dtxContextRegistry;

    private final RpcClient rpcClient;


    @Autowired
    public JoinGroupExecuteService(TransactionManager transactionManager,
                                   DTXContextRegistry dtxContextRegistry, RpcClient rpcClient) {
        this.transactionManager = transactionManager;
        this.dtxContextRegistry = dtxContextRegistry;
        this.rpcClient = rpcClient;
    }


    @Override
    public Serializable execute(TransactionCmd transactionCmd) throws TxManagerException {
        try {
            DTXContext dtxContext = dtxContextRegistry.get(transactionCmd.getGroupId());
            JoinGroupParams joinGroupParams = transactionCmd.getMsg().loadBean(JoinGroupParams.class);
            txLogger.txTrace(transactionCmd.getGroupId(), joinGroupParams.getUnitId(), "unit:{} try join group:{}",
                    joinGroupParams.getUnitId(), transactionCmd.getGroupId());
            transactionManager.join(dtxContext, joinGroupParams.getUnitId(), joinGroupParams.getUnitType(),
                    rpcClient.getAppName(transactionCmd.getRemoteKey()), joinGroupParams.getTransactionState());
            txLogger.txTrace(transactionCmd.getGroupId(), joinGroupParams.getUnitId(), "unit:{} joined.",
                    joinGroupParams.getUnitId());
        } catch (TransactionException e) {
            txLogger.error(this.getClass().getSimpleName(), e.getMessage());
            throw new TxManagerException(e.getLocalizedMessage());
        }
        // non response
        return null;
    }
}
@Override
    public void join(DTXContext dtxContext, String unitId, String unitType, String modId, int userState) throws TransactionException {
        //手动回滚时设置状态为回滚状态 0
        if (userState == 0) {
            dtxContext.resetTransactionState(0);
        }
        TransactionUnit transactionUnit = new TransactionUnit();
        transactionUnit.setModId(modId);
        transactionUnit.setUnitId(unitId);
        transactionUnit.setUnitType(unitType);
        dtxContext.join(transactionUnit);
    }
@Override
    public void join(TransactionUnit transactionUnit) throws TransactionException {
        try {
            fastStorage.saveTransactionUnitToGroup(groupId, transactionUnit);
        } catch (FastStorageException e) {
            throw new TransactionException("attempts to join the non-existent transaction group. ["
                    + transactionUnit.getUnitId() + '@' + transactionUnit.getModId() + ']');
        }
    }
@Override
    public void saveTransactionUnitToGroup(String groupId, TransactionUnit transactionUnit) throws FastStorageException {
        if (Optional.ofNullable(redisTemplate.hasKey(REDIS_GROUP_PREFIX + groupId)).orElse(false)) {
            redisTemplate.opsForHash().put(REDIS_GROUP_PREFIX + groupId, transactionUnit.getUnitId(), transactionUnit);
            return;
        }
        throw new FastStorageException("attempts to the non-existent transaction group " + groupId,
                FastStorageException.EX_CODE_NON_GROUP);
    }

lcn-manager服务端收到信息后会在redis与creategroup的hashkey相同数据下(hashkey为“tm:group:”+groupId)保存事务单元信息,key为事务单元id,值为事务单元信息。

3.3.4 业务执行成功后finally中的postBusinessCode:dtxLocalControl.postBusinessCode(info);

1)客户端发起方 LcnStartingTransaction 通知lcn-manager 服务端 处理事务组,然后服务端返回状态值state,根据状态值,客户端发起方再决定真正的提交事务还是回滚事务。客户端参与方 不加额外处理。

@Override
    public void postBusinessCode(TxTransactionInfo info) {
        // RPC close DTX group
        transactionControlTemplate.notifyGroup(
                info.getGroupId(), info.getUnitId(), info.getTransactionType(),
                DTXLocalContext.transactionState(globalContext.dtxState(info.getGroupId())));
    }
/**
     * Client通知事务组操作集合
     *
     * @param groupId         groupId
     * @param unitId          unitId
     * @param transactionType transactionType
     * @param state           transactionState
     */
    public void notifyGroup(String groupId, String unitId, String transactionType, int state) {
        try {
            txLogger.txTrace(
                    groupId, unitId, "notify group > transaction type: {}, state: {}.", transactionType, state);
            if (globalContext.isDTXTimeout()) {
                throw new LcnBusinessException("dtx timeout.");
            }
            //服务端返回不同的状态值 state
            state = reliableMessenger.notifyGroup(groupId, state);
           //清理事务 包含本地事务的提交和回滚 
           //执行不同代理LcnConnectionProxy的notify(int state)方法
            //细节见3.3.5
            transactionCleanTemplate.clean(groupId, unitId, transactionType, state);

        } catch (TransactionClearException e) {
            txLogger.trace(groupId, unitId, Transactions.TE, "clean transaction fail.");
        } catch (RpcException e) {
            dtxExceptionHandler.handleNotifyGroupMessageException(Arrays.asList(groupId, state, unitId, transactionType), e);
        } catch (LcnBusinessException e) {
            // 关闭事务组失败
            dtxExceptionHandler.handleNotifyGroupBusinessException(Arrays.asList(groupId, state, unitId, transactionType), e.getCause());
        }
        txLogger.txTrace(groupId, unitId, "notify group exception state {}.", state);
    }

注意:通知事务组进行提交事务这边比较绕。

 @Override
    public int notifyGroup(String groupId, int transactionState) throws RpcException, LcnBusinessException {
        NotifyGroupParams notifyGroupParams = new NotifyGroupParams();
        notifyGroupParams.setGroupId(groupId);
        notifyGroupParams.setState(transactionState);
        MessageDto messageDto = request0(MessageCreator.notifyGroup(notifyGroupParams),
                clientConfig.getTmRpcTimeout() * clientConfig.getChainLevel());
        // 成功清理发起方事务
        if (!MessageUtils.statusOk(messageDto)) {
            throw new LcnBusinessException(messageDto.loadBean(Throwable.class));
        }
        return messageDto.loadBean(Integer.class);
    }

同样是向lcn-manager服务端发送请求,服务端接收到请求后用NotifyGroupExecuteService类的execute方法进行处理。

@Service("rpc_notify-group")
public class NotifyGroupExecuteService implements RpcExecuteService {

    private static final TxLogger txLogger = TxLogger.newLogger(NotifyGroupExecuteService.class);

    private final TransactionManager transactionManager;

    private final DTXContextRegistry dtxContextRegistry;

    @Autowired
    public NotifyGroupExecuteService(TransactionManager transactionManager, DTXContextRegistry dtxContextRegistry) {
        this.transactionManager = transactionManager;
        this.dtxContextRegistry = dtxContextRegistry;
    }

    @Override
    public Serializable execute(TransactionCmd transactionCmd) throws TxManagerException {
        try {
            DTXContext dtxContext = dtxContextRegistry.get(transactionCmd.getGroupId());
            // 解析参数
            NotifyGroupParams notifyGroupParams = transactionCmd.getMsg().loadBean(NotifyGroupParams.class);
           //如果状态为1则事务成功,通知参与模块进行提交
            int commitState = notifyGroupParams.getState();
            // 获取事务状态(当手动回滚时会先设置状态)
            int transactionState = transactionManager.transactionStateFromFastStorage(transactionCmd.getGroupId());
            if (transactionState == 0) {
                commitState = 0;
            }

            // 系统日志
            txLogger.txTrace(
                    transactionCmd.getGroupId(), "", "notify group state: {}", notifyGroupParams.getState());
             //如果状态为1则事务成功,通知参与模块进行提交
            if (commitState == 1) {
                transactionManager.commit(dtxContext);
            } 
          //如果状态为0则事务失败,通知参与模块进行回滚
          else if (commitState == 0) { 
                transactionManager.rollback(dtxContext);
            }
            if (transactionState == 0) {
                txLogger.txTrace(transactionCmd.getGroupId(), "", "mandatory rollback for user.");
            }
            return commitState;
        } catch (TransactionException e) {
            throw new TxManagerException(e);
        } finally {
            transactionManager.close(transactionCmd.getGroupId());
            // 系统日志
            txLogger.txTrace(transactionCmd.getGroupId(), "", "notify group successfully.");
        }
    }
}
 @Override
    public void commit(DTXContext dtxContext) throws TransactionException {
        notifyTransaction(dtxContext, 1);
    }
private void notifyTransaction(DTXContext dtxContext, int transactionState) throws TransactionException {
        List transactionUnits = dtxContext.transactionUnits();
        log.debug("group[{}]'s transaction units: {}", dtxContext.getGroupId(), transactionUnits);
        for (TransactionUnit transUnit : transactionUnits) {
            NotifyUnitParams notifyUnitParams = new NotifyUnitParams();
            notifyUnitParams.setGroupId(dtxContext.getGroupId());
            notifyUnitParams.setUnitId(transUnit.getUnitId());
            notifyUnitParams.setUnitType(transUnit.getUnitType());
            notifyUnitParams.setState(transactionState);
            txLogger.txTrace(dtxContext.getGroupId(), notifyUnitParams.getUnitId(), "notify {}'s unit: {}",
                    transUnit.getModId(), transUnit.getUnitId());
            try {
                List modChannelKeys = rpcClient.remoteKeys(transUnit.getModId());
                if (modChannelKeys.isEmpty()) {
                    // record exception
                    throw new RpcException("offline mod.");
                }
                MessageDto respMsg =
                        rpcClient.request(modChannelKeys.get(0), MessageCreator.notifyUnit(notifyUnitParams));
                if (!MessageUtils.statusOk(respMsg)) {
                    // 提交/回滚失败的消息处理
                    List params = Arrays.asList(notifyUnitParams, transUnit.getModId());
                    rpcExceptionHandler.handleNotifyUnitBusinessException(params, respMsg.loadBean(Throwable.class));
                }
            } catch (RpcException e) {
                // 提交/回滚通讯失败
                List params = Arrays.asList(notifyUnitParams, transUnit.getModId());
                rpcExceptionHandler.handleNotifyUnitMessageException(params, e);
            } finally {
                txLogger.txTrace(dtxContext.getGroupId(), notifyUnitParams.getUnitId(), "notify unit over");
            }
        }
    } 
  

notifyTransaction方法又会调用客户端,客户端用DefaultNotifiedUnitService的execute方法进行处理

@Service("rpc_lcn_notify-unit")
@Slf4j
public class LcnNotifiedUnitService extends DefaultNotifiedUnitService {

    @Autowired
    public LcnNotifiedUnitService(TransactionCleanTemplate transactionCleanTemplate, TCGlobalContext context) {
        super(transactionCleanTemplate, context);
    }
}
public class DefaultNotifiedUnitService implements RpcExecuteService {

    private static final TxLogger txLogger = TxLogger.newLogger(DefaultNotifiedUnitService.class);

    private final TransactionCleanTemplate transactionCleanTemplate;

    private TCGlobalContext globalContext;

    public DefaultNotifiedUnitService(TransactionCleanTemplate transactionCleanTemplate, TCGlobalContext globalContext) {
        this.transactionCleanTemplate = transactionCleanTemplate;
        this.globalContext = globalContext;
    }

    @Override
    public Serializable execute(TransactionCmd transactionCmd) throws TxClientException {
        try {
            NotifyUnitParams notifyUnitParams = transactionCmd.getMsg().loadBean(NotifyUnitParams.class);
            // 保证业务线程执行完毕后执行事务清理操作
            TxContext txContext = globalContext.txContext(transactionCmd.getGroupId());
            if (Objects.nonNull(txContext)) {
                synchronized (txContext.getLock()) {
                    txLogger.txTrace(transactionCmd.getGroupId(), notifyUnitParams.getUnitId(),
                            "clean transaction cmd waiting for business code finish.");
                    txContext.getLock().wait();
                }
            }
            // 事务清理操作
            // 见3.3.5
            transactionCleanTemplate.clean(
                    notifyUnitParams.getGroupId(),
                    notifyUnitParams.getUnitId(),
                    notifyUnitParams.getUnitType(),
                    notifyUnitParams.getState());
            return true;
        } catch (TransactionClearException | InterruptedException e) {
            throw new TxClientException(e);
        }
    }
}

3.3.5  根据服务端返回的状态值state 客户端决定真正执行提交本地事务还是回滚

调用的方法为:transactionCleanTemplate.clean(groupId, unitId, transactionType, state);

/**
     * 清理事务
     *
     * @param groupId  groupId
     * @param unitId   unitId
     * @param unitType unitType
     * @param state    transactionState
     * @throws TransactionClearException TransactionClearException
     */
    public void clean(String groupId, String unitId, String unitType, int state) throws TransactionClearException {
        txLogger.txTrace(groupId, unitId, "clean transaction");
        try {
            cleanWithoutAspectLog(groupId, unitId, unitType, state);
            aspectLogger.clearLog(groupId, unitId);
        } catch (TransactionClearException e) {
            if (!e.isNeedCompensation()) {
                aspectLogger.clearLog(groupId, unitId);
            }
        } catch (Throwable throwable) {
            aspectLogger.clearLog(groupId, unitId);
        }
        txLogger.txTrace(groupId, unitId, "clean transaction over");
    }
/**
     * 清理事务(不清理切面日志)
     *
     * @param groupId  groupId
     * @param unitId   unitId
     * @param unitType unitType
     * @param state    transactionState
     * @throws TransactionClearException TransactionClearException
     */
    public void cleanWithoutAspectLog(String groupId, String unitId, String unitType, int state) throws TransactionClearException {
        try {
            transactionBeanHelper.loadTransactionCleanService(unitType).clear(
                    groupId, state, unitId, unitType
            );
        } finally {
            globalContext.clearGroup(groupId);

            dtxChecking.stopDelayChecking(groupId, unitId);
        }
    }
  /**
     * 事务清理业务
     *
     * @param groupId  groupId
     * @param state    事务状态 1 提交 0 回滚
     * @param unitId   unitId
     * @param unitType 事务类型
     * @throws TransactionClearException TransactionClearException
     */
    void clear(String groupId, int state, String unitId, String unitType) throws TransactionClearException;
@Component
@Slf4j
public class LcnTransactionCleanService implements TransactionCleanService {

    private final TCGlobalContext globalContext;

    @Autowired
    public LcnTransactionCleanService(TCGlobalContext globalContext) {
        this.globalContext = globalContext;
    }

    @Override
    public void clear(String groupId, int state, String unitId, String unitType) throws TransactionClearException {
        try {
            LcnConnectionProxy connectionProxy = globalContext.getLcnConnection(groupId);
            connectionProxy.notify(state);
            // todo notify exception
        } catch (TCGlobalContextException e) {
            log.warn("Non lcn connection when clear transaction.");
        }
    }
}
@Slf4j
public class LcnConnectionProxy implements Connection {

    private Connection connection;

    public LcnConnectionProxy(Connection connection) {
        this.connection = connection;
    }

    /**
     * notify connection
     *
     * @param state transactionState
     * @return RpcResponseState RpcResponseState
     */
    public RpcResponseState notify(int state) {
        try {
            if (state == 1) {
                log.debug("commit transaction type[lcn] proxy connection:{}.", this);
                connection.commit();
            } else {
                log.debug("rollback transaction type[lcn] proxy connection:{}.", this);
                connection.rollback();
            }
            connection.close();
            log.debug("transaction type[lcn] proxy connection:{} closed.", this);
            return RpcResponseState.success;
        } catch (Exception e) {
            log.error(e.getLocalizedMessage(), e);
            return RpcResponseState.fail;
        }
    }
    /*
    *   省略其他代码
    *
    */
}

 

你可能感兴趣的:(分布式事务,TX-LCN)