关于seata:Seata分布式事务失败通知

77次阅读

共计 2951 个字符,预计需要花费 8 分钟才能阅读完成。

一、背景

在咱们应用 Seata 作为分布式事务时,有些时候咱们的分布式时候并不是每次都能够胜利的,而对于这些失败的分布式事务就须要进行告诉。这篇文章简略记录一下如何实现告诉。

二、性能实现

  1. 此处模仿邮件告诉,然而不真正发送邮件,只是简略记录一个日志。

三、注意事项

1、假如咱们的分布式事务回滚失败,在 AT 模式中是会锁定表记录数据的。前期须要获取这条记录的 全局锁 操作,都会失败。

举例:

假如存在如下数据表记录数据

账号 金额
zhangsan 100

zhangsan这条记录参加分布式事务。

  1. 在分布式事务中将 zhangsan 的金额批改成 90.
  2. 另外一个共事,间接操作数据库,将 zhangsan 的记录批改成 80.(此时是能够批改胜利的,因为 Seata 是二阶段提交,在第一阶段完结后,会开释记录的本地锁,不开释记录的全局锁)
  3. 接下来持续别的分布式事务操作,然而产生了异样,此时分布式事务会回滚失败,因为 zhangsan 的金额变成了 80, 和之前的对应不上。
  4. 如果再次对 zhangsan 进行分布式事务操作或者须要获取全局锁的操作,那么都会失败。

四、实现步骤

1、编写一个类实现 FailureHandler 接口

FailureHandler 的全类名为:io.seata.tm.api.FailureHandler,它有一个默认的实现DefaultFailureHandlerImpl, 此处咱们写一个类继承这个类。

package com.huan.seata.handler;

import io.seata.tm.api.DefaultFailureHandlerImpl;
import io.seata.tm.api.GlobalTransaction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * Seata 分布式事物失败的解决
 *
 * @author huan.fu 2021/10/8 - 下午 1:51
 */
@Component("failureHandler")
@Slf4j
public class EmailSeataFailureHandler extends DefaultFailureHandlerImpl {
    
    @Override
    public void onBeginFailure(GlobalTransaction tx, Throwable cause) {super.onBeginFailure(tx, cause);
        log.warn("邮件告诉: 分布式事物出现异常:[onBeginFailure],xid:[{}]", tx.getXid());
    }
    
    @Override
    public void onCommitFailure(GlobalTransaction tx, Throwable cause) {super.onCommitFailure(tx, cause);
        log.warn("邮件告诉: 分布式事物出现异常:[onCommitFailure],xid:[{}]", tx.getXid());
    }
    
    @Override
    public void onRollbackFailure(GlobalTransaction tx, Throwable originalException) {super.onRollbackFailure(tx, originalException);
        log.warn("邮件告诉: 分布式事物出现异常:[onRollbackFailure],xid:[{}]", tx.getXid());
    }
    
    @Override
    public void onRollbackRetrying(GlobalTransaction tx, Throwable originalException) {super.onRollbackRetrying(tx, originalException);
        log.warn("邮件告诉: 分布式事物出现异常:[onRollbackRetrying],xid:[{}]", tx.getXid());
    }
}

2、退出到 Spring 中的 BeanName 的值

在测试的时候发现 FailureHandler 退出到 Spring 中的 beanName 必须是failureHandler,否则报错,是 seata 的主动配置中如下代码决定的。

BEAN_NAME_FAILURE_HANDLER 常量的值为 failureHandler


/**
 * 源码
 * The type Seata auto configuration
 */
@ConditionalOnProperty(prefix = SEATA_PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class SeataAutoConfiguration {private static final Logger LOGGER = LoggerFactory.getLogger(SeataAutoConfiguration.class);

    @Bean(BEAN_NAME_FAILURE_HANDLER)
    @ConditionalOnMissingBean(FailureHandler.class)
    public FailureHandler failureHandler() {return new DefaultFailureHandlerImpl();
    }

    @Bean
    @DependsOn({BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER, BEAN_NAME_FAILURE_HANDLER})
    @ConditionalOnMissingBean(GlobalTransactionScanner.class)
    public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataProperties, FailureHandler failureHandler) {if (LOGGER.isInfoEnabled()) {LOGGER.info("Automatically configure Seata");
        }
        return new GlobalTransactionScanner(seataProperties.getApplicationId(), seataProperties.getTxServiceGroup(), failureHandler);
    }

}

五、测试

拜访curl -X GET http://localhost:50027/createOrder\?accountId\=1\&amount\=10\&hasException\=true,而后手动打断点,批改数据库数据,使数据不统一,而后代码跑出异样,此处回滚失败,因为 undo_log 表中的数据对不起来。

六、残缺代码

https://gitee.com/huan1993/spring-cloud-parent/tree/master/seata/seata-springboot-failure-handler

正文完
 0