继上篇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操作。