当前位置:首页 > 科技  > 软件

借助Nacos高效配置与实践Seata事务的TCC模式

来源: 责编: 时间:2024-02-01 12:45:10 286观看
导读实现TCC 模式TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:Try:资源的检测和预留;Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。Cancel:预留资源

实现

TCC 模式

TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:MZ828资讯网——每日最新资讯28at.com

  • Try:资源的检测和预留;
  • Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。
  • Cancel:预留资源释放,可以理解为try的反向操作。

流程分析

图片图片MZ828资讯网——每日最新资讯28at.com

阶段一(Try):检查余额是否充足,如果充足则冻结金额增加30元,可用余额扣除30MZ828资讯网——每日最新资讯28at.com

图片图片MZ828资讯网——每日最新资讯28at.com

图片图片MZ828资讯网——每日最新资讯28at.com

此时,总金额 = 冻结金额 + 可用金额,数量依然是100不变,事务直接提交无需等待其它事务。MZ828资讯网——每日最新资讯28at.com

阶段二(Confirm) :假如要提交,则冻结金额扣减30MZ828资讯网——每日最新资讯28at.com

图片图片MZ828资讯网——每日最新资讯28at.com

确认可以提交,不过之前可用金额已经扣减过了,这里只要清除冻结金额就好了,此时,总金额 = 冻结金额 + 可用金额 = 0 + 70 = 70MZ828资讯网——每日最新资讯28at.com

阶段二(Cancel):如果要回滚,则冻结金额扣减30,可用余额增加30MZ828资讯网——每日最新资讯28at.com

图片图片MZ828资讯网——每日最新资讯28at.com

需要回滚,那么就要释放冻结金额,恢复可用金额MZ828资讯网——每日最新资讯28at.com

Seata的TCC模型

图片图片MZ828资讯网——每日最新资讯28at.com

代码样例

配置和依赖参考之前《利用Nacos实现Seata事务模式(XA与AT)的快速配置与灵活切换》即可MZ828资讯网——每日最新资讯28at.com

bank3:MZ828资讯网——每日最新资讯28at.com

声明TCC接口@LocalTCCpublic 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);}

具体实现:MZ828资讯网——每日最新资讯28at.com

@Componentpublic 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;        }        // 避免空悬挂,已经执行过回滚了就不能再预留资源        if (TccActionResultWrap.hasRollbackResult(xid) || TccActionResultWrap.hasCommitResult(xid)) {            return false;        }        // 预留资源        boolean result = accountInfoMapper.prepareDeductMoney(accountNo,amount) > 0;        // 记录执行结果,以便回滚时判断是否是空回滚        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");        // 执行提交操作,扣除预留款        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;        }        // 执行回滚        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;    }}

业务层:MZ828资讯网——每日最新资讯28at.com

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

参数中的BusinessActionContext不需要开发人员自己传递,直接给null即可,Seata会自动处理。MZ828资讯网——每日最新资讯28at.com

mapper:MZ828资讯网——每日最新资讯28at.com

@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服务调用:MZ828资讯网——每日最新资讯28at.com

@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的优点:MZ828资讯网——每日最新资讯28at.com

  • 一阶段完成直接提交事务,释放数据库资源,性能好
  • 相比AT模型,无需生成快照,无需使用全局锁,性能最强
  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

TCC的缺点:MZ828资讯网——每日最新资讯28at.com

  • 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
  • 软状态,事务是最终一致
  • 需要考虑Confirm和Cancel的失败情况,做好幂等处理
  • 空回滚:当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚
  • 业务悬挂:对于已经空回滚的业务,之前被阻塞的try操作恢复,继续执行try,就永远不可能confirm或cancel ,事务一直处于中间状态,这就是业务悬挂。

图片图片MZ828资讯网——每日最新资讯28at.com

执行cancel操作时,应当判断try是否已经执行,如果尚未执行,则应该空回滚。MZ828资讯网——每日最新资讯28at.com

执行try操作时,应当判断cancel是否已经执行过了,如果已经执行,应当阻止空回滚后的try操作,避免悬挂。MZ828资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-70394-0.html借助Nacos高效配置与实践Seata事务的TCC模式

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: PHP 高性能的事件循环库 Revolt

下一篇: Vue3问题:如何实现页面引导提示?

标签:
  • 热门焦点
  • 一加Ace2 Pro官宣:普及16G内存 引领24G

    一加官方今天继续为本月发布的新机一加Ace2 Pro带来预热,公布了内存方面的信息。“淘汰 8GB ,12GB 起步,16GB 普及,24GB 引领,还有呢?#一加Ace2Pro#,2023 年 8 月,敬请期待。”同时
  • 俄罗斯:将审查iPhone等外国公司设备 保数据安全

    iPhone和特斯拉都属于在各自领域领头羊的品牌,推出的产品也也都是数一数二的,但对于一些国家而言,它们的产品可靠性和安全性还是在限制范围内。近日,俄罗斯联邦通信、信息技术
  • 十个可以手动编写的 JavaScript 数组 API

    JavaScript 中有很多API,使用得当,会很方便,省力不少。 你知道它的原理吗? 今天这篇文章,我们将对它们进行一次小总结。现在开始吧。1.forEach()forEach()用于遍历数组接收一参
  • 在线图片编辑器,支持PSD解析、AI抠图等

    自从我上次分享一个人开发仿造稿定设计的图片编辑器到现在,不知不觉已过去一年时间了,期间我经历了裁员失业、面试找工作碰壁,寒冬下一直没有很好地履行计划.....这些就放在日
  • 东方甄选单飞:有些鸟注定是关不住的

    文/彭宽鸿编辑/罗卿东方甄选创始人俞敏洪带队的&ldquo;7天甘肃行&rdquo;直播活动已在近日顺利收官。成立后一年多时间里,东方甄选要脱离抖音自立门户的传闻不绝于耳,&ldquo;7
  • 网红炒股不为了赚钱,那就是耍流氓!

    来源:首席商业评论6月26日高调宣布入市,网络名嘴大v胡锡进居然进军了股市。在一次财经媒体峰会上,几个财经圈媒体大佬就&ldquo;胡锡进炒股是否知道认真报道&rdquo;展开讨论。有
  • 小米公益基金会捐赠2500万元驰援北京、河北暴雨救灾

    8月2日消息,今日小米科技创始人雷军在其微博上发布消息称,小米公益基金会宣布捐赠2500万元驰援北京、河北暴雨救灾。携手抗灾,京冀安康!以下为公告原文
  • 英特尔Xe-HP项目终止,将专注Xe-HPC/HPG系列显卡

    据10 月 31 日消息报道,英特尔高级副总裁兼加速计算系统和图形事业部总经理 表示,Xe-HP“ Arctic Sound” 系列服务器 GPU 已经应用于 oneAPI devcloud 云服
  • 三翼鸟智能家居亮相电博会,让用户体验更真实

    2021电博会在青岛国际会展中心开幕中,三翼鸟直接把“家”搬到了现场,成为了展会的一大看点。这也是三翼鸟继9月9日发布了行业首个一站式定制智慧家平台后的
Top