1.背景当我们使用Seata作为分布式事务时,有时我们的分布式事务不能每次都成功,我们需要通知这些失败的分布式事务。本文简单记录下如何实现通知。2.功能实现这里模拟了邮件通知,但是并没有真正发送邮件,只是简单的记录了一条日志。三、注意事项1、假设我们的分布式事务回滚失败,表记录数据会被AT模式锁定。以后需要获取这条记录的全局锁操作将会失败。示例:假设存在如下数据表,记录数据账户金额zhangsan100zhangsan。该记录参与分布式事务。在分布式事务中,将zhangsan的数量修改为90,另一位同事直接操作数据库,将zhangsan的记录修改为80。(此时可以修改成功,因为seata是两阶段提交,结束在第一阶段之后,会释放记录的本地锁,不会释放记录的全局锁)接下来,继续其他分布式事务操作,但是出现异常,分布式事务回滚失败这时回来,因为张三的数量变成了80,和之前的不对应了。如果再对zhangsan进行分布式事务操作或者需要获取全局锁的操作,就会失败。四、实现步骤1、编写一个类,实现FailureHandler接口。FailureHandler的全类名是:io.seata.tm.api.FailureHandler,默认实现了DefaultFailureHandlerImpl。这里我们写一个类来继承这个类。包com.huan.seata.handler;导入io.seata.tm.api.DefaultFailureHandlerImpl;导入io.seata.tm.api.GlobalTransaction;导入lombok.extern.slf4j.Slf4j;导入org.springframework.stereotype.Component;/***Seata分布式事务失败处理**@authorhuan.fu2021/10/8-1:51PM*/@Component("failureHandler")@Slf4jpublicclassEmailSeataFailureHandlerextendsDefaultFailureHandlerImpl{@OverridepublicvoidonBeginFailure(GlobalTransactiontx,Throwablecause){super.onBeginFailure(tx,cause);log.warn("邮件通知:分布式事物异常:[onBeginFailure],xid:[{}]",tx.getXid());}@OverridepublicvoidonCommitFailure(GlobalTransactiontx,Throwablecause){super.onCommitFailure(tx,cause);log.warn("邮件通知:分布式事物异常:[onCommitFailure],xid:[{}]",tx.getXid());}@OverridepublicvoidonRollbackFailure(GlobalTransactiontx,ThrowableoriginalException){super.onRollbackFailure(tx,originalException);日志。warn("邮件通知:分布式事物异常:[onRollbackFailure],xid:[{}]",tx.getXid());}@OverridepublicvoidonRollbackRetrying(GlobalTransactiontx,ThrowableoriginalException){super.onRollbackRetrying(tx,originalException);log.warn("邮件通知:分布式事物异常:[onRollbackRetrying],xid:[{}]",tx.getXid());}}2.Spring添加的BeanName的值在测试的时候发现FailureHandler添加到Spring的beanName必须是failureHandler,否则报错,seata的自动配置中有如下代码判断BEAN_NAME_FAILURE_HANDLER常量的值为failureHandler/***源码*Seata自动配置类型*/@ConditionalOnProperty(prefix=SEATA_PREFIX,name="enabled",havingValue="true",matchIfMissing=true)publicclassSeataAutoConfiguration{privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(SeataAutoConfiguration.class);@Bean(BEAN_NAME_FAILURE_HANDLER)@ConditionalOnMissingBean(FailureHandler.class)publicFailureHandlerfailureHandler(){返回新的DefaultFailureHandlerImpl();}@Bean@DependsOn({BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER,BEAN_NAME_FAILURE_HANDLER})@ConditionalOnMissingBean(GlobalTransactionScanner.class)publicGlobalTransactionScannerglobalTransactionScanner(SeataPropertiesseataProperties,FailureHandlerfailureHandler){如果(LOGGER.isInfotamaticallyconfigureSeamatic()){(LOGGER);}返回新的GlobalTransactionScanner(seataProperties.getApplicationId(),seataProperties.getTxServiceGroup(),failureHandler);}}五、测试访问curl-XGEThttp://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
