本文將介紹基於springcloud+feign使用阿里巴巴分佈式事務框架seata的TCC模式(1.0.0版本) ,AT模式基本上能滿足我們使用分佈式事務80%的需求。 但涉及非關係型數據庫與中間件的操作、跨公司服務的調用跨語言的應用調用需要結合TCC模式。關於seata的介紹可以點擊這裏進入seata官網。
一、TCC模式的概念
一個分佈式的全局事務,整體是兩階段提交(Try-[Comfirm/Cancel])的模型。在SEATA中,AT模式與TCC模式事實上都是基於兩階段提交實現。
他們的區別在於:
AT 模式基於 支持本地 ACID 事務 的 關係型數據庫:
- 一階段 prepare 行爲:在本地事務中,一併提交業務數據更新和相應回滾日誌記錄。
- 二階段 commit 行爲:馬上成功結束,自動 異步批量清理回滾日誌。
- 二階段 rollback 行爲:通過回滾日誌,自動 生成補償操作,完成數據回滾。
相應的,TCC 模式,不依賴於底層數據資源的事務支持:
- 一階段 prepare 行爲:調用 自定義 的 prepare 邏輯。
- 二階段 commit 行爲:調用 自定義 的 commit 邏輯。
- 二階段 rollback 行爲:調用 自定義 的 rollback 邏輯。
所謂 TCC 模式,是指支持把 自定義 的分支事務納入到全局事務的管理中。
簡單點概括,SEATA的TCC模式就是手工的AT模式,它允許你自定義兩階段的處理邏輯而不依賴AT模式的undo_log。
二、TM與TCC-RM的搭建
準備nacos、seata服務端(TC), 着重講基於SpringCloud+Feign的TCC的實現,項目的搭建直接看源碼
1、seata服務端的搭建(TC事務協調者,略)
2、TM的搭建(TM全局事務控制,略)
3、TCC-RM的搭建(RM分支事務控制)
3.1 定義TCC接口
由於我們使用的是SpringCloud+Feign,Feign的調用基於http,因此此處我們使用LocalTCC便可。值得注意的是,@LocalTCC一定需要註解在接口上,此接口可以是尋常的業務接口,只要實現了TCC的兩階段提交對應方法便可。
- @LocalTCC 適用於SpringCloud+Feign模式下的TCC
- @TwoPhaseBusinessAction 註解try方法,其中name爲當前tcc方法的bean名稱,寫方法名便可(記得全局唯一),commitMethod指向提交方法,rollbackMethod指向事務回滾方法。指定好三個方法之後,seata會根據事務的成功或失敗,通過動態代理去幫我們自動調用提交或者回滾。
- @BusinessActionContextParameter 註解可以將參數傳遞到二階段(commitMethod/rollbackMethod)的方法。
- BusinessActionContext 便是指TCC事務上下文
/**
* 這裏定義tcc的接口
* 一定要定義在接口上
* 我們使用springCloud的遠程調用
* 那麼這裏使用LocalTCC便可
*
* @author tanzj
*/
@LocalTCC
public interface TccService {
/**
* 定義兩階段提交
* name = 該tcc的bean名稱,全局唯一
* commitMethod = commit 爲二階段確認方法
* rollbackMethod = rollback 爲二階段取消方法
* BusinessActionContextParameter註解 可傳遞參數到二階段方法
*
* @param params -入參
* @return String
*/
@TwoPhaseBusinessAction(name = "insert", commitMethod = "commitTcc", rollbackMethod = "cancel")
String insert(
@BusinessActionContextParameter(paramName = "params") Map params
);
/**
* 確認方法、可以另命名,但要保證與commitMethod一致
* context可以傳遞try方法的參數
*
* @param context 上下文
* @return boolean
*/
boolean commitTcc(BusinessActionContext context);
/**
* 二階段取消方法
*
* @param context 上下文
* @return boolean
*/
boolean cancel(BusinessActionContext context);
}
3.2 TCC接口的業務實現
爲了保證代碼的簡潔,此處將Controller與Service結合講解,實際項目則不然。
- 在try方法中使用@Transational可以直接通過spring事務回滾關係型數據庫中的操作,而非關係型數據庫等中間件的回滾操作可以交給rollbackMethod方法處理。
- 使用context.getActionContext("params")便可以得到一階段try中定義的參數,在二階段對此參數進行業務回滾操作。
- 注意,此處亦不可以捕獲異常(同理切面處理異常),否則TCC將識別該操作爲成功,二階段直接執行commitMethod。
- 二階段commitMethod可以空確認。
@Slf4j
@RestController
public class TccServiceImpl implements TccService {
@Autowired
TccDAO tccDAO;
/**
* tcc服務t(try)方法
* 實際業務方法
*
* @param params - name
* @return String
*/
@Override
@PostMapping("/tcc-insert")
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public String insert(@RequestBody Map params) {
log.info("------------------> xid = " + RootContext.getXID());
//實際的操作,或操作MQ、redis等
tccDAO.insert(params);
//throw new RuntimeException("服務tcc測試回滾");
return "success";
}
/**
* tcc服務 confirm方法
* 可以空確認
*
* @param context 上下文
* @return boolean
*/
@Override
public boolean commitTcc(BusinessActionContext context) {
log.info("xid = " + context.getXid() + "提交成功");
return true;
}
/**
* tcc 服務 cancel方法
*
* @param context 上下文
* @return boolean
*/
@Override
public boolean cancel(BusinessActionContext context) {
//todo 這裏寫中間件、非關係型數據庫的回滾操作
System.out.println("please manually rollback this data:" + context.getActionContext("params"));
return true;
}
}
3.3 seata相關yml配置(單機版)
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: seataGroup-${seata.application-id}
service:
vgroup-mapping: default
grouplist: 127.0.0.1:8091
config:
type: file
file:
name: file.conf
registry:
type: nacos
file:
name: file.conf
至此完成seata-tcc模式的配置工作。