1. 概述

本文分享 TCC 恢复。主要涉及如下二个 package 路径下的类:

  • org.mengyun.tcctransaction.recover
  • RecoverConfig,事务恢复配置接口
  • TransactionRecovery,事务恢复逻辑
  • org.mengyun.tcctransaction.spring.recover :
  • DefaultRecoverConfig,默认事务恢复配置实现
  • RecoverScheduledJob,事务恢复定时任务

本文涉及到的类关系如下图( 打开大图 ):

在《TCC-Transaction 源码分析 —— 事务存储器》中,事务信息被持久化到外部的存储器中。事务存储是事务恢复的基础。通过读取外部存储器中的异常事务,定时任务会按照一定频率对事务进行重试,直到事务完成或超过最大重试次数。

你行好事会因为得到赞赏而愉悦同理,开源项目贡献者会因为 Star 而更加有动力为 TCC-Transaction 点赞!传送门

ps:笔者假设你已经阅读过《tcc-transaction 官方文档 —— 使用指南1.2.x》。

2. 事务重试配置

org.mengyun.tcctransaction.recover.RecoverConfig,事务恢复配置接口,实现代码如下:

public interface RecoverConfig {

/**

* @return 最大重试次数

*/

int getMaxRetryCount();

/**

* @return 恢复间隔时间,单位:秒

*/

int getRecoverDuration();

/**

* @return cron 表达式

*/

String getCronExpression();

/**

* @return 延迟取消异常集合

*/

Set> getDelayCancelExceptions();

/**

* 设置延迟取消异常集合

*

* @param delayRecoverExceptions 延迟取消异常集合

*/

void setDelayCancelExceptions(Set> delayRecoverExceptions);

}

  • #getMaxRetryCount(),单个事务恢复最大重试次数。超过最大重试次数后,目前仅打出错误日志,下文会看到实现。
  • #getRecoverDuration(),单个事务恢复重试的间隔时间,单位:秒。
  • #getCronExpression(),定时任务 cron 表达式。
  • #getDelayCancelExceptions(),延迟取消异常集合。

org.mengyun.tcctransaction.spring.recover.DefaultRecoverConfig,默认事务恢复配置实现,实现代码如下:

public class DefaultRecoverConfig implements RecoverConfig {

public static final RecoverConfig INSTANCE = new DefaultRecoverConfig();

/**

* 最大重试次数

*/

private int maxRetryCount = 30;

/**

* 恢复间隔时间,单位:秒

*/

private int recoverDuration = 120;

/**

* cron 表达式

*/

private String cronExpression = "0 */1 * * * ?";

/**

* 延迟取消异常集合

*/

private Set> delayCancelExceptions = new HashSet>();

public DefaultRecoverConfig() {

delayCancelExceptions.add(OptimisticLockException.class);

delayCancelExceptions.add(SocketTimeoutException.class);

}

@Override

public void setDelayCancelExceptions(Set> delayCancelExceptions) {

this.delayCancelExceptions.addAll(delayCancelExceptions);

}

}

  • maxRetryCount,单个事务恢复最大重试次数 为 30。
  • recoverDuration,单个事务恢复重试的间隔时间为 120 秒。
  • cronExpression,定时任务 cron 表达式为 "0 */1 * * * ?",每分钟执行一次。如果你希望定时任务执行的更频繁,可以修改 cron 表达式,例如 0/30 * * * * ?,每 30 秒执行一次。
  • delayCancelExceptions,延迟取消异常集合。在 DefaultRecoverConfig 构造方法里,预先添加了 OptimisticLockException / SocketTimeoutException 。
  • 官方解释:事务恢复的疑问
  • 这块笔者还有一些疑问,如果有别的可能性导致这个情况,麻烦告知下笔者。谢谢。
  • 官方解释:为什么 tcc 事务切面中对乐观锁与socket超时异常不做回滚处理,只抛异常?
  • 针对 SocketTimeoutException :try 阶段,本地参与者调用远程参与者( 远程服务,例如 Dubbo,Http 服务),远程参与者 try 阶段的方法逻辑执行时间较长,超过 Socket 等待时长,发生 SocketTimeoutException,如果立刻执行事务回滚,远程参与者 try 的方法未执行完成,可能导致 cancel 的方法实际未执行( try 的方法未执行完成,数据库事务【非 TCC 事务】未提交,cancel 的方法读取数据时发现未变更,导致方法实际未执行,最终 try 的方法执行完后,提交数据库事务【非 TCC 事务】,较为极端 ),最终引起数据不一致。在事务恢复时,会对这种情况的事务进行取消回滚,如果此时远程参与者的 try 的方法还未结束,还是可能发生数据不一致。
  • 针对 OptimisticLockException :还是 SocketTimeoutException 的情况,事务恢复间隔时间小于 Socket 超时时间,此时事务恢复调用远程参与者取消回滚事务,远程参与者下次更新事务时,会因为乐观锁更新失败,抛出 OptimisticLockException。如果 CompensableTransactionInterceptor 此时立刻取消回滚,可能会和定时任务的取消回滚冲突,因此统一交给定时任务处理。

3. 事务重试定时任务

org.mengyun.tcctransaction.spring.recover.RecoverScheduledJob,事务恢复定时任务,基于 Quartz 实现调度,不断不断不断执行事务恢复。实现代码如下:

public class RecoverScheduledJob {

private TransactionRecovery transactionRecovery;

private TransactionConfigurator transactionConfigurator;

private Scheduler scheduler;

public void init() {

try {

// Quartz JobDetail

MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();

jobDetail.setTargetObject(transactionRecovery);

jobDetail.setTargetMethod("startRecover");

jobDetail.setName("transactionRecoveryJob");

jobDetail.setConcurrent(false); // 禁止并发

jobDetail.afterPropertiesSet();

// Quartz CronTriggerFactoryBean

CronTriggerFactoryBean cronTrigger = new CronTriggerFactoryBean();

cronTrigger.setBeanName("transactionRecoveryCronTrigger");

cronTrigger.setCronExpression(transactionConfigurator.getRecoverConfig().getCronExpression());

cronTrigger.setJobDetail(jobDetail.getObject());

cronTrigger.afterPropertiesSet();

// 启动任务调度

scheduler.scheduleJob(jobDetail.getObject(), cronTrigger.getObject());

// 启动 Quartz Scheduler

scheduler.start();

} catch (Exception e) {

throw new SystemException(e);

}

}

}

  • 调用 MethodInvokingJobDetailFactoryBean#setConcurrent(false) 方法,禁用任务并发执行。
  • 调用 MethodInvokingJobDetailFactoryBean#setTargetObject(...) + MethodInvokingJobDetailFactoryBean#setTargetMethod(...) 方法,设置任务调用 TransactionRecovery#startRecover(...) 方法执行。

如果应用集群部署,会不会相同事务被多个定时任务同时重试

答案是不会,事务在重试时会乐观锁更新,同时只有一个应用节点能更新成功。

官方解释:多机部署下,所有机器都宕机,从异常中恢复时,所有的机器岂不是都可以查询到所有的需要恢复的服务?

当然极端情况下,Socket 调用超时时间大于事务重试间隔,第一个节点在重试某个事务,一直未执行完成,第二个节点已经可以重试。

ps:建议,Socket 调用超时时间小于事务重试间隔。

是否定时任务和应用服务器解耦

蚂蚁金服的分布式事务服务 DTS 采用 client-server 模式:

  • xts-client :负责事务的创建、提交、回滚、记录。
  • xts-server :负责异常事务的恢复。

FROM 《蚂蚁金融云 DTS 文档》分布式事务服务 (Distributed Transaction Service, DTS) 是一个分布式事务框架,用来保障在大规模分布式环境下事务的最终一致性。DTS 从架构上分为 xts-client 和 xts-server 两部分,前者是一个嵌入客户端应用的 JAR 包,主要负责事务数据的写入和处理;后者是一个独立的系统,主要负责异常事务的恢复。

4. 异常事务恢复

org.mengyun.tcctransaction.recover.TransactionRecovery,异常事务恢复,实现主体代码如下:

```Java

public class TransactionRecovery {

/**

* 启动恢复事务逻辑

*/

public void startRecover() {

// 加载异常事务集合

List transactions = loadErrorTransactions();

// 恢复异常事务集合

recoverErrorTransactions(transactions);

}

}

```

4.1 加载异常事务集合

调用 #loadErrorTransactions() 方法,加载异常事务集合。实现代码如下:

private List loadErrorTransactions() {

TransactionRepository transactionRepository = transactionConfigurator.getTransactionRepository();

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

RecoverConfig recoverConfig = transactionConfigurator.getRecoverConfig();

return transactionRepository.findAllUnmodifiedSince(new Date(currentTimeInMillis - recoverConfig.getRecoverDuration() * 1000));

}

  • 异常事务的定义:当前时间超过 - 事务变更时间( 最后执行时间 ) >= 事务恢复间隔( RecoverConfig#getRecoverDuration() )。这里有一点要注意,已完成的事务会从事务存储器删除。

4.2 恢复异常事务集合

调用 #recoverErrorTransactions(...) 方法,恢复异常事务集合。实现代码如下:

private void recoverErrorTransactions(List 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 content:%s

事务回滚什么意思 try_分布式事务 TCC-Transaction 源码分析——事务恢复相关推荐

  1. spring transaction源码分析--事务架构

    1. 引言  事务特性 事务是并发控制的单元,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性.事 ...

  2. Spring中@Transactional事务回滚(含实例详细讲解,附源码)

    一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部门里面有很多成员,这两者分别保存在部门表和成员表里面,在删除 ...

  3. zookeeper源码分析之恢复事务日志

    zookeeper源码分析之恢复事务日志 前言 源码分析 查看事务日志命令 总结 前言 本文是基于zookeeper集群启动过程分析(https://blog.csdn.net/weixin_4244 ...

  4. 事务回滚什么意思 try_三问Spring事务:解决什么问题?如何解决?存在什么问题?...

    1. 解决什么问题 让我们先从事务说起,"什么是事务?我们为什么需要事务?".事务是一组无法被分割的操作,要么所有操作全部成功,要么全部失败.我们在开发中需要通过事务将一些操作组成 ...

  5. python django事务transaction源码分析

    2019独角兽企业重金招聘Python工程师标准>>> python Django事务 网上关于django1.6的事务资料很多,但是1.8的却搜不到任何资料,自己要用的时候费了不少 ...

  6. RocketMQ 源码分析 事务消息

    为什么80%的码农都做不了架构师?>>>    1. 概述 必须必须必须 前置阅读内容: <事务消息(阿里云)> 2. 事务消息发送 2.1 Producer 发送事务消 ...

  7. redisson分布式限流[RRateLimiter]源码分析

    接下来在讲一讲平时用的比较多的限流模块–RRateLimiter 1.简单使用 public static void main(String[] args) throws InterruptedExc ...

  8. php try 并回滚,ThinkPHP异常处理、事务处理(事务回滚)

    本篇文章给大家介绍,ThinkPHP异常处理.事务处理(事务回滚),用购买下订单减库存的案例分析,希望对大家在工作和学习中有所帮助. 1.Mysql建表引擎使用InnoDB,支持事务回滚. 2.代码案 ...

  9. Spring中的事务回滚 网上比较不错的文章

    1 浅谈Spring中的事务回滚 https://www.cnblogs.com/zeng1994/p/8257763.html 2 spring 事务回滚 https://www.cnblogs.c ...

最新文章

  1. 解决工控网络通信协议威胁的实践
  2. (转)C#网络编程(基本概念和操作) - Part.1
  3. java json和对象互相装换
  4. C语言libcurl例程:multi 多线程,多任务
  5. Pytorch:Tensor和Numpy
  6. 3d激光雷达开发(PassThrough滤波器)
  7. Jenkins+git 实现代码自动发布
  8. 1.material组件的安装及其使用
  9. -webkit-padding-start: 40px;ul的padding-left:40px;问题
  10. vue||简易版音乐播放器
  11. 分词:词性标注北大标准
  12. linux学习笔记(十二)
  13. Matlab图形中输入希腊字母
  14. 格密码LLL算法:如何解决最短向量SVP问题(2)
  15. 计算机未连接到互联网(win11系统)
  16. pacman源添加及yaourt安装
  17. 检测图像中的椭圆 并求其长短轴...
  18. Windows服务器更改远程端口3389
  19. Thread.Sleep(0)的妙用
  20. 二进制,八进制,十进制,十六进制转换算法

热门文章

  1. OpenGL vs D3D
  2. SQL Server CheckPoint的几个误区
  3. 用cascade删除有约束的表或记录
  4. Android蓝牙串口程序开发
  5. CentOS6.5下安装Apache2.4+PHP7
  6. winform记事本初步实现
  7. AIX系统CPU性能评估-1
  8. 面向程序员的数据库访问性能优化法则
  9. ip地址管理与子网的划分二
  10. Azure Backup和Azure Site Recovery的区别是什么