分散式事務解決方案(五)【TCC型方案】
5-TCC型方案
5.1 介紹
TCC方案屬於兩階段型/補償型
5.1.1 實現
- 一個完整的業務活動由一個主業務服務與若干從業務服務組成
- 主業務服務負責發起並完成整個業務活動
- 從業務服務提供TCC型業務操作
- 業務活動管理器控制業務活動的一致性,它登記業務活動中的操作,並在業務活動提交時確認所有的TCC型操作的confirm操作,在業務活動取消時呼叫所有TCC型操作的cancel操作.
5.1.2 成本
- 業務活動結束時confirm或cancel操作的執行成本
- 業務活動日誌成本
5.1.3 適用範圍
- 強隔離性,嚴格一致性要求的業務活動
- 適用於執行時間較短的業務,如處理賬戶,收費等業務
5.1.4 用到的服務模式
- TCC操作
- 冪等操作
- 可補償操作
- 可查詢操作
5.1.5 方案特點
- 不與具體的服務框架耦合(RPC架構通用)
- 位於業務服務層,而非資源層
- 可以靈活選擇業務資源的鎖定粒度
- TCC裡對每個服務資源操作的是本地事務,資料被lock的時間短,可擴充套件性好(可以說是為獨立部署的SOA服務而設計的)
5.1.6 行業應用案例
- 支付寶XTS(螞蟻金融雲的分散式事務服務DTS)
5.2 TCC框架
GitHub上一個開源的TCC框架實現,以此專案為基礎講解TCC框架的實現(主要是在專案原始碼上新增一些註釋)
5.3 TCC應用例項
5.3.1 業務流程
以訂單處理流程為例:賬戶扣款->使用紅包優惠券->訂單狀態改變為例
一個主服務呼叫從業務服務,被TCC控制的方法都應該具有三種方法:主方法(try方法)/確認方法(confirm方法)/取消方法(cancel方法)
- 修改支付記錄狀態
- 修改訂單狀態
- 遠端呼叫點券服務
- 遠端呼叫積分服務
- 若遠端呼叫存在異常
5.2.2 使用TCC-TRASACTION示例
@Service("pointAccountService")
public class pointAccountServiceImpl implements pointAccountService{
private static final Logger LOG = LoggerFactory.getLogger(pointAccountServiceImpl.class);
@Autowired
private pointAccountDao pointAccountDao;
@Autowired
private pointAccountHistoryDao pointAccountHistoryDao;
@Override
public void saveData(pointAccount pointAccount) {
pointAccountDao.insert(pointAccount);
}
@Override
public void updateData(pointAccount pointAccount) {
pointAccountDao.update(pointAccount);
}
/**
* 積分賬戶加款 Trying
* @param transactionContext
* @param userNo
* @param pointAmount
* @param requestNo
* @param bankTrxNo
* @param trxType
* @param remark
* @throws BizException
*/
@Override
@Transactional(rollbackFor = Exception.class)
@Compensable(confirmMethod = "confirmCreditToPointAccountTcc",cancelMethod = "cancelCreditToPointAccountTcc")
public void creditToPointAccountTcc(TransactionContext transactionContext, String userNo, Integer pointAmount, String requestNo, String bankTrxNo, String trxType, String remark) throws BizException {
LOG.info("===>creditToPointAccountTcc TRYING begin");
//根據商戶編號獲取商戶積分賬戶
pointAccount pointAccount = pointAccountDao.getByUserNo(userNo);
if (pointAccount == null){//如果不存在商戶積分賬戶,建立一條新的積分賬戶
pointAccount = new pointAccount();
pointAccount.setBalance(0);
pointAccount.setUserNo(userNo);
pointAccount.setStatus(PublicEnum.YES.name());
pointAccount.setCreateTime(new Date());
pointAccount.setId(StringUtil.get32UUID());
pointAccountDao.insert(pointAccount);
}
pointAccountHistory pointAccountHistory = pointAccountHistoryDao.getByRequestNo(requestNo);
// 冪等判斷
if ( pointAccountHistory == null ){//防止多次提交
pointAccountHistory = new pointAccountHistory();
pointAccountHistory.setId(StringUtil.get32UUID());
pointAccountHistory.setCreateTime(new Date());
pointAccountHistory.setStatus(PointAccountHistoryStatusEnum.TRYING.name());//訊息不可用
pointAccountHistory.setAmount(pointAmount);///積分賬戶變動額
pointAccountHistory.setBalance(pointAccount.getBalance() + pointAmount);
pointAccountHistory.setBankTrxNo(bankTrxNo);//銀行流水號
pointAccountHistory.setRequestNo(requestNo);//請求號
pointAccountHistory.setFundDirection(PointAccountFundDirectionEnum.ADD.name());
pointAccountHistory.setTrxType(trxType);
pointAccountHistory.setRemark(remark);
pointAccountHistory.setUserNo(userNo);
pointAccountHistoryDao.insert(pointAccountHistory);
}else if (PointAccountHistoryStatusEnum.CANCEL.name().equals(pointAccountHistory.getStatus())){
//如果是取消的,有可能是之前的業務出現異常問題而取消,那麼重試階段,再將狀態更新為TYING狀態,而不是重新建立一條
LOG.info("之前因為業務問題取消後,又重試的{}" , pointAccountHistory.getBankTrxNo());
pointAccountHistory.setStatus(PointAccountHistoryStatusEnum.TRYING.name());
this.pointAccountHistoryDao.update(pointAccountHistory);
}
//新增一條不可用的積分賬戶流水
LOG.info("===>creditToPointAccountTcc TRYING end");
}
/**
* 積分賬戶增加確認
* @param transactionContext
* @param userNo
* @param pointAmount
* @param requestNo
* @param bankTrxNo
* @param trxType
* @param remark
* @return
* @throws BizException
*/
@Transactional(rollbackFor = Exception.class)
public void confirmCreditToPointAccountTcc(TransactionContext transactionContext, String userNo, Integer pointAmount, String requestNo, String bankTrxNo, String trxType, String remark) throws BizException {
LOG.info("===>confirmCreditToPointAccountTcc begin");
//根據請求號獲取賬戶基本流水
pointAccountHistory pointAccountHistory = pointAccountHistoryDao.getByRequestNo(requestNo);
// 冪等判斷
if ( pointAccountHistory == null || PointAccountHistoryStatusEnum.CONFORM.name().equals(pointAccountHistory.getStatus())){//該筆交易流水已處理過,不需再處理
return;
}
pointAccountHistory.setStatus(PointAccountHistoryStatusEnum.CONFORM.name());
pointAccountHistoryDao.update(pointAccountHistory);
pointAccount pointAccount = pointAccountDao.getByUserNo(userNo);//獲取使用者積分賬戶
pointAccount.setBalance(pointAccount.getBalance() + pointAmount);//增加賬戶餘額
pointAccountDao.update(pointAccount);
LOG.info("===>confirmCreditToPointAccountTcc end");
}
/**
*積分賬戶增加回滾
* @param transactionContext
* @param userNo
* @param pointAmount
* @param requestNo
* @param bankTrxNo
* @param trxType
* @param remark
* @throws BizException
*/
@Transactional(rollbackFor = Exception.class)
public void cancelCreditToPointAccountTcc(TransactionContext transactionContext, String userNo, Integer pointAmount, String requestNo, String bankTrxNo, String trxType, String remark) throws BizException {
LOG.info("===>cancelCreditToPointAccountTcc begin");
pointAccountHistory pointAccountHistory = pointAccountHistoryDao.getByRequestNo(requestNo);
// 冪等判斷
if ( pointAccountHistory == null || !PointAccountHistoryStatusEnum.TRYING.name().equals(pointAccountHistory.getStatus())){//該筆交易流水已處理過,不需再處理
return;
}
pointAccountHistory.setStatus(PointAccountHistoryStatusEnum.CANCEL.name());
pointAccountHistoryDao.update(pointAccountHistory);
LOG.info("===>cancelCreditToPointAccountTcc end");
}
/**
* 積分賬戶加款 Trying
* @param userNo
* @param pointAmount
* @param requestNo
* @param bankTrxNo
* @param trxType
* @param remark
* @throws BizException
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void creditToPointAccount(String userNo, Integer pointAmount, String requestNo, String bankTrxNo, String trxType, String remark) throws BizException {
//根據商戶編號獲取商戶積分賬戶
pointAccount pointAccount = pointAccountDao.getByUserNo(userNo);
if (pointAccount == null){//如果不存在商戶積分賬戶,建立一條新的積分賬戶
pointAccount = new pointAccount();
pointAccount.setBalance(0);
pointAccount.setUserNo(userNo);
pointAccount.setStatus(PublicEnum.YES.name());
pointAccount.setCreateTime(new Date());
pointAccount.setId(StringUtil.get32UUID());
pointAccountDao.insert(pointAccount);
}
//新增一條積分歷史
pointAccountHistory pointAccountHistory = pointAccountHistoryDao.getByRequestNo(requestNo);
if ( pointAccountHistory == null ){//防止多次提交
pointAccountHistory = new pointAccountHistory();
pointAccountHistory.setId(StringUtil.get32UUID());
pointAccountHistory.setCreateTime(new Date());
pointAccountHistory.setStatus(PointAccountHistoryStatusEnum.CONFORM.name());//可用
pointAccountHistory.setAmount(pointAmount);///積分賬戶變動額
pointAccountHistory.setBalance(pointAccount.getBalance() + pointAmount);
pointAccountHistory.setBankTrxNo(bankTrxNo);//銀行流水號
pointAccountHistory.setRequestNo(requestNo);//請求號
pointAccountHistory.setFundDirection(PointAccountFundDirectionEnum.ADD.name());
pointAccountHistory.setTrxType(trxType);
pointAccountHistory.setRemark(remark);
pointAccountHistory.setUserNo(userNo);
pointAccountHistoryDao.insert(pointAccountHistory);
}
//增加積分賬戶
pointAccount.setBalance(pointAccount.getBalance() + pointAmount);//增加賬戶餘額
pointAccountDao.update(pointAccount);
}
@Override
public pointAccount getDataById(String id) {
return pointAccountDao.getById(id);
}
@Override
public PageBean listPage(PageParam pageParam, pointAccount pointAccount) {
Map<String, Object> paramMap = new HashMap<String, Object>();
return pointAccountDao.listPage(pageParam, paramMap);
}
}
相關文章
- 分散式事務解決方案分散式
- SpringCloud 分散式事務解決方案SpringGCCloud分散式
- 五種分散式事務解決方案(圖文總結)分散式
- 分散式事務解決方案彙總分散式
- MSSQL server分散式事務解決方案SQLServer分散式
- 常用的分散式事務解決方案分散式
- 分散式事務解決方案--GTS(二)分散式
- 分散式事務解決方案--GTS(一)分散式
- 分散式柔性事務的TCC方案分散式
- 分散式事務解決方案(一)【介紹】分散式
- 微服務架構及分散式事務解決方案微服務架構分散式
- 基於RocketMq的分散式事務解決方案MQ分散式
- 分散式事務解決方案(四)【最大努力通知】分散式
- 分散式事務(2)---強一致性分散式事務解決方案分散式
- 微服務分散式事務4種解決方案實戰微服務分散式
- 分散式事務(4)---最終一致性方案之TCC分散式
- 你必須瞭解的分散式事務解決方案分散式
- 分散式事務解決方案——柔性事務與服務模式分散式模式
- 分散式事務概述及大廠通用解決方案分散式
- 分散式事務(四)之TCC分散式
- 微服務分散式事務解決方案-開源軟體seata微服務分散式
- 微服務架構下分散式事務解決方案-hoop(一)微服務架構分散式OOP
- 分散式事務的概念和解決方案Seate分散式
- 阿里巴巴開源分散式事務解決方案 Fescar阿里分散式
- 來了!阿里開源分散式事務解決方案Fescar阿里分散式
- 來了!阿里開源分散式事務解決方案 Fescar阿里分散式
- 分散式事務解決方案與適用場景分析分散式
- 分散式事務處理方案,微服事務處理方案分散式
- 分散式事務(2)---TCC理論分散式
- .NET開源的處理分散式事務的解決方案分散式
- 分散式事務的理解和常見解決方案彙總分散式
- 架構師必備的那些分散式事務解決方案!!架構分散式
- 阿里巴巴開源分散式事務解決方案 FESCAR【轉】阿里分散式
- 剛柔並濟的開源分散式事務解決方案分散式
- 常用的分散式事務解決方案介紹有多少種?分散式
- Java微服務下的分散式事務介紹及其解決方案2Java微服務分散式
- 五大分散式場景解決方案分散式
- 關於分散式事務帶來的問題及解決方案分散式