一、Seata TCC 模式
一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的:
一阶段 prepare 行为
二阶段 commit 或 rollback 行为
根据两阶段行为模式的不同,我们将分支事务划分为 Automatic (Branch) Transaction Mode 和 TCC (Branch) Transaction Mode.
AT 模式(参考链接 TBD)基于 支持本地 ACID 事务 的 关系型数据库:
一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。
相应的,TCC 模式,不依赖于底层数据资源的事务支持:
一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
二阶段 commit 行为:调用 自定义 的 commit 逻辑。
二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。
二、TCC模式实现
@LocalTCC 必须注解在接口类上,适用于SpringCloud+Feign模式下的TCC
@TwoPhaseBusinessAction 注解try方法,其中name为当前tcc方法的bean名称,写方法名便可(记得全局唯一),commitMethod指向提交方法,rollbackMethod指向事务回滚方法。指定好三个方法之后,seata会根据全局事务的成功或失败,去帮我们自动调用提交方法或者回滚方法。
@BusinessActionContextParameter 注解可以将参数传递到二阶段(commitMethod/rollbackMethod)的方法。
BusinessActionContext 便是指TCC事务上下文
1、定义TCC接口
@LocalTCC
public interface TccActionOne {
/**
* Prepare boolean.
*
* @param actionContext the action context
* @param a the a
* @return the boolean
*/
@TwoPhaseBusinessAction(name = "TccActionOne", commitMethod = "commit", rollbackMethod = "rollback")
public boolean prepare(BusinessActionContext actionContext,
@BusinessActionContextParameter(paramName = "a") int a);
/**
* Commit boolean.
*
* @param actionContext the action context
* @return the boolean
*/
public boolean commit(BusinessActionContext actionContext);
/**
* Rollback boolean.
*
* @param actionContext the action context
* @return the boolean
*/
public boolean rollback(BusinessActionContext actionContext);
}
@LocalTCC
public interface TccActionTwo {
/**
* Prepare boolean.
*
* @param actionContext the action context
* @param b the b
* @param list the list
* @return the boolean
*/
@TwoPhaseBusinessAction(name = "TccActionTwo", commitMethod = "commit", rollbackMethod = "rollback")
public boolean prepare(BusinessActionContext actionContext,
@BusinessActionContextParameter(paramName = "b") String b,
@BusinessActionContextParameter(paramName = "c", index = 1) List list);
/**
* Commit boolean.
*
* @param actionContext the action context
* @return the boolean
*/
public boolean commit(BusinessActionContext actionContext);
/**
* Rollback boolean.
*
* @param actionContext the action context
* @return the boolean
*/
public boolean rollback(BusinessActionContext actionContext);
}
public class ResultHolder {
private static Map<String, String> actionOneResults = new ConcurrentHashMap<String, String>();
private static Map<String, String> actionTwoResults = new ConcurrentHashMap<String, String>();
/**
* Set action one result.
*
* @param txId the tx id
* @param result the result
*/
public static void setActionOneResult(String txId, String result) {
actionOneResults.put(txId, result);
}
/**
* Get action one result string.
*
* @param txId the tx id
* @return the string
*/
public static String getActionOneResult(String txId) {
return actionOneResults.get(txId);
}
/**
* Set action two result.
*
* @param txId the tx id
* @param result the result
*/
public static void setActionTwoResult(String txId, String result) {
actionTwoResults.put(txId, result);
}
/**
* Get action two result string.
*
* @param txId the tx id
* @return the string
*/
public static String getActionTwoResult(String txId) {
return actionTwoResults.get(txId);
}
}
创建实现类
@Service
public class TccActionOneImpl implements TccActionOne {
@Override
public boolean prepare(BusinessActionContext actionContext, int a) {
String xid = actionContext.getXid();
System.out.println("TccActionOne prepare, xid:" + xid + ", a:" + a);
return true;
}
@Override
public boolean commit(BusinessActionContext actionContext) {
String xid = actionContext.getXid();
System.out.println("TccActionOne commit, xid:" + xid + ", a:" + actionContext.getActionContext("a"));
ResultHolder.setActionOneResult(xid, "T");
return true;
}
@Override
public boolean rollback(BusinessActionContext actionContext) {
String xid = actionContext.getXid();
System.out.println("TccActionOne rollback, xid:" + xid + ", a:" + actionContext.getActionContext("a"));
ResultHolder.setActionOneResult(xid, "R");
return true;
}
}
@Service
public class TccActionTwoImpl implements TccActionTwo {
@Override
public boolean prepare(BusinessActionContext actionContext, String b, List list) {
String xid = actionContext.getXid();
System.out.println("TccActionTwo prepare, xid:" + xid + ", b:" + b + ", c:" + list.get(1));
return true;
}
@Override
public boolean commit(BusinessActionContext actionContext) {
String xid = actionContext.getXid();
System.out.println("TccActionTwo commit, xid:" + xid + ", b:" + actionContext.getActionContext("b") + ", c:" + actionContext.getActionContext("c"));
ResultHolder.setActionTwoResult(xid, "T");
return true;
}
@Override
public boolean rollback(BusinessActionContext actionContext) {
String xid = actionContext.getXid();
System.out.println("TccActionTwo rollback, xid:" + xid + ", b:" + actionContext.getActionContext("b") + ", c:" + actionContext.getActionContext("c"));
ResultHolder.setActionTwoResult(xid, "R");
return true;
}
}
创建调用tcc接口类,这里我们编写了两个方法,一个是提交方法,一个是回滚方法。
并使用@GlobalTransactional注释
@Service
public class TccTransactionService {
@Autowired
private TccActionOne tccActionOne;
@Autowired
private TccActionTwo tccActionTwo;
/**
* 发起分布式事务
*
* @return string string
*/
@GlobalTransactional
public String doTransactionCommit() {
//第一个TCC 事务参与者
boolean result = tccActionOne.prepare(null, 1);
if (!result) {
throw new RuntimeException("TccActionOne failed.");
}
List list = new ArrayList();
list.add("c1");
list.add("c2");
result = tccActionTwo.prepare(null, "two", list);
if (!result) {
throw new RuntimeException("TccActionTwo failed.");
}
return RootContext.getXID();
}
/**
* Do transaction rollback string.
*
* @param map the map
* @return the string
*/
@GlobalTransactional
public String doTransactionRollback(Map map) {
//第一个TCC 事务参与者
boolean result = tccActionOne.prepare(null, 1);
if (!result) {
throw new RuntimeException("TccActionOne failed.");
}
List list = new ArrayList();
list.add("c1");
list.add("c2");
result = tccActionTwo.prepare(null, "two", list);
if (!result) {
throw new RuntimeException("TccActionTwo failed.");
}
map.put("xid", RootContext.getXID());
throw new RuntimeException("transacton rollback");
}
}
在创建个controller类去测试我们的提交和回滚是否正常工作
@RequestMapping("tcc/commit")
public String commit() {
String txId = tccTransactionService.doTransactionCommit();
return txId;
}
@RequestMapping("tcc/rollback")
public String rollback() {
Map map = new HashMap(16);
try {
tccTransactionService.doTransactionRollback(map);
Assert.isTrue(false, "分布式事务未回滚");
} catch (Throwable t) {
Assert.isTrue(true, "分布式事务异常回滚");
System.out.println(t.getMessage());
}
String txId = (String)map.get("xid");
return txId;
}