自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

借助Nacos高效配置與實踐Seata事務的TCC模式

開發(fā) 前端
對于已經空回滾的業(yè)務,之前被阻塞的try操作恢復,繼續(xù)執(zhí)行try,就永遠不可能confirm或cancel ,事務一直處于中間狀態(tài),這就是業(yè)務懸掛。

實現

TCC 模式

TCC模式與AT模式非常相似,每階段都是獨立事務,不同的是TCC通過人工編碼來實現數據恢復。需要實現三個方法:

  • Try:資源的檢測和預留;
  • Confirm:完成資源操作業(yè)務;要求 Try 成功 Confirm 一定要能成功。
  • Cancel:預留資源釋放,可以理解為try的反向操作。

流程分析

圖片圖片

階段一(Try):檢查余額是否充足,如果充足則凍結金額增加30元,可用余額扣除30

圖片圖片

圖片圖片

此時,總金額 = 凍結金額 + 可用金額,數量依然是100不變,事務直接提交無需等待其它事務。

階段二(Confirm) :假如要提交,則凍結金額扣減30

圖片圖片

確認可以提交,不過之前可用金額已經扣減過了,這里只要清除凍結金額就好了,此時,總金額 = 凍結金額 + 可用金額 = 0 + 70 = 70

階段二(Cancel):如果要回滾,則凍結金額扣減30,可用余額增加30

圖片圖片

需要回滾,那么就要釋放凍結金額,恢復可用金額

Seata的TCC模型

圖片圖片

代碼樣例

配置和依賴參考之前《利用Nacos實現Seata事務模式(XA與AT)的快速配置與靈活切換》即可

bank3:

聲明TCC接口

@LocalTCC
public interface AccountInTcc {

    @TwoPhaseBusinessAction(name = "prepareDeductMoney", commitMethod = "commitDeductMoney", rollbackMethod = "rollbackDeductMoney")
    boolean prepareDeductMoney(BusinessActionContext businessActionContext,
                               @BusinessActionContextParameter(paramName = "accountNo")String accountNo,
                               @BusinessActionContextParameter(paramName = "amount")Double amount);

    /**
     * 提交扣款
     * 二階段confirm確認方法、可以另命名,但要保證與commitMethod一致
     */
    boolean commitDeductMoney(BusinessActionContext businessActionContext);
    /**
     * 回滾扣款
     * 二階段回滾方法,要保證與rollbackMethod一致
     */
    boolean rollbackDeductMoney(BusinessActionContext businessActionContext);
}

具體實現:

@Component
public class AccountInTccImpl implements AccountInTcc {

    @Autowired
    private AccountInfoMapper accountInfoMapper;

    @Transactional
    @Override
    public boolean prepareDeductMoney(BusinessActionContext businessActionContext, String accountNo, Double amount) {
        String xid = businessActionContext.getXid();
        // 冪等性判斷
        if (TccActionResultWrap.hasPrepareResult(xid)) {
            return true;
        }

        // 避免空懸掛,已經執(zhí)行過回滾了就不能再預留資源
        if (TccActionResultWrap.hasRollbackResult(xid) || TccActionResultWrap.hasCommitResult(xid)) {
            return false;
        }
        // 預留資源
        boolean result = accountInfoMapper.prepareDeductMoney(accountNo,amount) > 0;

        // 記錄執(zhí)行結果,以便回滾時判斷是否是空回滾
        TccActionResultWrap.prepareSuccess(xid);
        System.out.println("============prepare==============");
        return result;
    }

    // 保證提交邏輯的原子性
    @Transactional
    @Override
    public boolean commitDeductMoney(BusinessActionContext businessActionContext) {
        String xid = businessActionContext.getXid();
        // 冪等性判斷
        if (TccActionResultWrap.hasCommitResult(xid)) {
            return true;
        }
        Map<String, Object> actionContext = businessActionContext.getActionContext();
        String accountNo = (String) actionContext.get("accountNo");
        BigDecimal amount = (BigDecimal) actionContext.get("amount");
        // 執(zhí)行提交操作,扣除預留款
        boolean result = accountInfoMapper.commitDeductMoney(accountNo,amount.doubleValue()) > 0;
        // 清除預留結果
        TccActionResultWrap.removePrepareResult(xid);
        // 設置提交結果
        TccActionResultWrap.commitSuccess(xid);
        System.out.println("============commit==============");
        return result;
    }

    @Transactional
    @Override
    public boolean rollbackDeductMoney(BusinessActionContext businessActionContext) {
        String xid = businessActionContext.getXid();
        // 冪等性判斷
        if (TccActionResultWrap.hasRollbackResult(xid)) {
            return true;
        }
        // 沒有預留資源結果,回滾不做任何處理;
        if (!TccActionResultWrap.hasPrepareResult(xid)) {
            // 設置回滾結果,防止空回滾
            TccActionResultWrap.rollbackSuccess(xid);
            return true;
        }
        // 執(zhí)行回滾
        Map<String, Object> actionContext = businessActionContext.getActionContext();
        String accountNo = (String) actionContext.get("accountNo");
        BigDecimal amount = (BigDecimal) actionContext.get("amount");
        boolean result = accountInfoMapper.rollbackDeductMoney(accountNo,amount.doubleValue()) > 0;
        // 清除預留結果
        TccActionResultWrap.removePrepareResult(xid);
        // 設置回滾結果
        TccActionResultWrap.rollbackSuccess(xid);
        System.out.println("============rollback==============");
        return result;
    }
}

業(yè)務層:

@Autowired
  private AccountInTcc accountInTcc;

  @Override
  public Boolean deductMoney(String accountNo, Double amount) {
      return accountInTcc.prepareDeductMoney(null,accountNo,amount);
  }

參數中的BusinessActionContext不需要開發(fā)人員自己傳遞,直接給null即可,Seata會自動處理。

mapper:

@Update("update account_info set account_balance = account_balance - #{amount}, frozen_money = frozen_money + #{amount} where account_no = #{accountNo} and account_balance >= #{amount}")
    int prepareDeductMoney(@Param("accountNo") String accountNo, @Param("amount") Double amount);
    @Update("update account_info set frozen_money = frozen_money - #{amount} where account_no = #{accountNo}")
    int commitDeductMoney(@Param("accountNo") String accountNo, @Param("amount") Double amount);
    @Update("update account_info set account_balance = account_balance + #{amount}, frozen_money = frozen_money - #{amount} where account_no = #{accountNo}")
    int rollbackDeductMoney(@Param("accountNo") String accountNo, @Param("amount") Double amount);

bank4服務調用:

@GlobalTransactional
    @Override
    public Boolean addMoney(String accountNo, Double amount) {

        String result = bank3Client.deduct(amount);
        if("true".equalsIgnoreCase(result)){
            Boolean flag = baseMapper.addMoney(accountNo,amount) > 0;
            if(amount != 30 ) throw new RuntimeException("bank4 make exception amount != 30");
            return flag;
        }
        return false;
    }

TCC的優(yōu)點:

  • 一階段完成直接提交事務,釋放數據庫資源,性能好
  • 相比AT模型,無需生成快照,無需使用全局鎖,性能最強
  • 不依賴數據庫事務,而是依賴補償操作,可以用于非事務型數據庫

TCC的缺點:

  • 有代碼侵入,需要人為編寫try、Confirm和Cancel接口,太麻煩
  • 軟狀態(tài),事務是最終一致
  • 需要考慮Confirm和Cancel的失敗情況,做好冪等處理
  • 空回滾:當某分支事務的try階段阻塞時,可能導致全局事務超時而觸發(fā)二階段的cancel操作。在未執(zhí)行try操作時先執(zhí)行了cancel操作,這時cancel不能做回滾,就是空回滾
  • 業(yè)務懸掛:對于已經空回滾的業(yè)務,之前被阻塞的try操作恢復,繼續(xù)執(zhí)行try,就永遠不可能confirm或cancel ,事務一直處于中間狀態(tài),這就是業(yè)務懸掛。

圖片圖片

執(zhí)行cancel操作時,應當判斷try是否已經執(zhí)行,如果尚未執(zhí)行,則應該空回滾。

執(zhí)行try操作時,應當判斷cancel是否已經執(zhí)行過了,如果已經執(zhí)行,應當阻止空回滾后的try操作,避免懸掛。

責任編輯:武曉燕 來源: 一安未來
相關推薦

2024-01-30 08:10:37

Nacos事務模式

2022-01-12 10:02:02

TCC模式 Seata

2024-10-09 14:14:07

2025-04-30 10:44:02

2023-05-17 00:15:11

TCCXA模式

2021-12-27 09:20:13

事務模式隔離

2025-02-08 10:56:18

2023-07-26 08:25:02

2025-04-28 00:44:04

2021-11-14 16:07:35

中間件阿里Seata

2022-06-21 08:27:22

Seata分布式事務

2022-07-03 14:03:57

分布式Seata

2020-04-28 12:18:08

Seata模式分布式

2024-12-09 09:35:00

2021-04-23 08:15:51

Seata XA AT

2023-10-24 08:25:20

TCC模式事務

2022-06-20 11:05:49

TCC模式commit

2024-08-30 09:27:35

2019-05-16 09:00:06

云原生監(jiān)控日志管理

2023-08-16 11:43:57

數據引擎
點贊
收藏

51CTO技術棧公眾號