分布式事务之TCC模型 confirm失败补偿

继上篇https://blog.csdn.net/qq_20958071/article/details/86527850

上篇说到try阶段失败的时候,事务实现回滚,利用cancel方法保证事务的一致性,但是在confirm阶段,confirmA成功,confirmB成功,confirmC失败或者方法执行时间过长(也许是网络原因导致)的时候,怎么来保证事务的一致性呢。

TCC有一个任务,专门用来做confirm阶段事务的任务的恢复org.mengyun.tcctransaction.recover.TransactionRecovery类。

<!-- 事务恢复 -->
    <bean id="transactionRecovery" class="org.mengyun.tcctransaction.recover.TransactionRecovery">
        <property name="transactionConfigurator" ref="tccTransactionConfigurator"/>
    </bean>
<!-- TCC事务配置器 -->
    <bean id="tccTransactionConfigurator" class="org.mengyun.tcctransaction.spring.support.TccTransactionConfigurator">
    </bean>
<!-- 启用定时任务注解 -->
    <task:annotation-driven/>

	<!-- 事务恢复任务调度器 -->
    <bean id="recoverScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"/>

	<!-- 事务恢复调度任务,初始化方法:init -->
    <bean id="recoverScheduledJob" class="org.mengyun.tcctransaction.spring.recover.RecoverScheduledJob" init-method="init">
        <property name="transactionConfigurator" ref="tccTransactionConfigurator"/>
        <property name="transactionRecovery" ref="transactionRecovery"/>
        <property name="scheduler" ref="recoverScheduler"/>
    </bean>

下面来看TransactionRecovery类的两个方法。

 /**
     * 找出所有执行错误的事务信息
     * @return
     */
    private List<Transaction> loadErrorTransactions() {

        TransactionRepository transactionRepository = transactionConfigurator.getTransactionRepository();

        long currentTimeInMillis = Calendar.getInstance().getTimeInMillis();

        List<Transaction> transactions = transactionRepository.findAllUnmodifiedSince(new Date(currentTimeInMillis - transactionConfigurator.getRecoverConfig().getRecoverDuration() * 1000));

        List<Transaction> recoverTransactions = new ArrayList<Transaction>();

        for (Transaction transaction : transactions) {
        	// 检验记录是否已经被修改(版本校验)
            int result = transactionRepository.update(transaction);

            if (result > 0) {
                recoverTransactions.add(transaction);
            }
        }
        
        // 日志输出,调试用
        if (!transactions.isEmpty()){
        	logger.debug("==>loadErrorTransactions transactions size:" + transactions.size());
        }

        return recoverTransactions;
    }

上面代码是在跟事务所分配的数据库表中,查询事务创建的时间与当前时间间隔(XX)秒以上的事务。

然后执行事务恢复的方法。

 /**
     * 恢复错误的事务.
     * @param transactions
     */
    private void recoverErrorTransactions(List<Transaction> transactions) {


        for (Transaction transaction : transactions) {

            if (transaction.getRetriedCount() > transactionConfigurator.getRecoverConfig().getMaxRetryCount()) {
            	// 超过次数的,跳过
                logger.error(String.format("recover failed with max retry count,will not try again. txid:%s, status:%s,retried count:%d", transaction.getXid(), transaction.getStatus().getId(), transaction.getRetriedCount()));
                continue;
            }

            try {
                transaction.addRetriedCount(); // 重试次数+1

                if (transaction.getStatus().equals(TransactionStatus.CONFIRMING)) {
                	// 如果是CONFIRMING(2)状态,则将事务往前执行
                    transaction.changeStatus(TransactionStatus.CONFIRMING);
                    transactionConfigurator.getTransactionRepository().update(transaction);
                    transaction.commit();

                } else {
                	// 其他情况,把事务状态改为CANCELLING(3),然后执行回滚
                    transaction.changeStatus(TransactionStatus.CANCELLING);
                    transactionConfigurator.getTransactionRepository().update(transaction);
                    transaction.rollback();
                }
                
                // 已成功恢复处理或其他情况,超时没处理的事务日志直接删除
                transactionConfigurator.getTransactionRepository().delete(transaction);
            } catch (Throwable e) {
                logger.warn(String.format("recover failed, txid:%s, status:%s,retried count:%d", transaction.getXid(), transaction.getStatus().getId(), transaction.getRetriedCount()), e);
            }
        }
    }

将错误的事务查询出来之后,如果事务进行到confirm阶段失败,则调用 transaction.commit(),即所有的参与者都执行一遍confirm操作,如果不是confirm阶段失败,就对事务进行rollback,即所有的参与者都执行一遍cancel操作。

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
TCCTry-Confirm-Cancel)是一种分布式事务解决方案,它通过将一个事务拆分成三个阶段(TryConfirm、Cancel)来保证事务的一致性。而在分布式环境下,TCC通常使用3PC协议来实现。 3PC(Three-Phase Commit)协议是一种常用的分布式事务协议,它将一个分布式事务分为三个阶段(CanCommit、PreCommit、DoCommit),并通过协调者(Coordinator)和参与者(Participant)之间的消息交换来实现事务的原子性和一致性。 具体来说,TCC使用的3PC协议流程如下: 1. CanCommit 阶段:协调者向参与者发送CanCommit请求,询问参与者是否可以执行该事务。如果参与者可以执行,则发送Yes响应,否则发送No响应。 2. PreCommit 阶段:协调者向参与者发送PreCommit请求,告诉参与者可以执行该事务,并让它准备好提交或撤销事务。如果参与者准备好了,则发送Ack响应。 3. DoCommit 阶段:协调者向参与者发送DoCommit请求,告诉参与者提交该事务。如果参与者成功提交了事务,则发送Ack响应,否则发送Abort响应。 如果任何一个阶段出现问题,协调者会向参与者发送Cancel请求,撤销事务。 需要注意的是,在TCC中,Try阶段和Cancel阶段由应用程序自己来实现,3PC协议主要用于Confirm和DoCommit阶段的协调。此外,由于3PC协议的复杂性和性能问题,TCC并不适用于所有的分布式事务场景,开发者需要根据具体的业务需求来选择合适的解决方案。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值