分布式事务TCC方案Hmily——springcloud + feign + mybatis

分布式事务TCC方案Hmily——springcloud + feign + mybatisTCC理论:分布式事务基础理论——TCCHmily介绍:分布式事务TCC方案——Hmily金融级柔性分布式事务解决方案介绍本文demo代码:GitHub依赖<dependency><groupId>org.dromara</groupId><artifactId>hmily-springcloud</artifactId><vers

大家好,又见面了,我是你们的朋友全栈君。

TCC理论:分布式事务基础理论——TCC

Hmily介绍:分布式事务TCC方案——Hmily金融级柔性分布式事务解决方案介绍

本文demo代码:GitHub

依赖

        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>hmily-springcloud</artifactId>
            <version>2.1.1</version>
        </dependency>

        <dependency>
            <groupId>org.dromara</groupId>
            <artifactId>hmily-spring-boot-starter-springcloud</artifactId>
            <version>2.1.1</version>
        </dependency>

配置

配置具体请根据自身需要参考:官方配置详解

本文实例以本地配置文件模式,hmily.yml如下:

hmily:
  server:
    configMode: local
    appName: account1
  #  如果server.configMode eq local 的时候才会读取到这里的配置信息.
  config:
    appName: account1
    serializer: kryo
    contextTransmittalMode: threadLocal
    scheduledThreadMax: 16
    scheduledRecoveryDelay: 60
    scheduledCleanDelay: 60
    scheduledPhyDeletedDelay: 600
    scheduledInitDelay: 30
    recoverDelayTime: 60
    cleanDelayTime: 180
    limit: 200
    retryMax: 10
    bufferSize: 8192
    consumerThreads: 16
    asyncRepository: true
    autoSql: true
    phyDeleted: true
    storeDays: 3
    repository: mysql

repository:
  database:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/hmily?autoReconnect=true&autoReconnectForPools=true&useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: lyl512240816
    maxActive: 20
    minIdle: 10
    connectionTimeout: 30000
    idleTimeout: 600000
    maxLifetime: 1800000
  file:
    path:
    prefix: /hmily
  mongo:
    databaseName:
    url:
    userName:
    password:
  zookeeper:
    host: localhost:2181
    sessionTimeOut: 1000
    rootPath: /hmily
  redis:
    cluster: false
    sentinel: false
    clusterUrl:
    sentinelUrl:
    masterName:
    hostName:
    port:
    password:
    maxTotal: 8
    maxIdle: 8
    minIdle: 2
    maxWaitMillis: -1
    minEvictableIdleTimeMillis: 1800000
    softMinEvictableIdleTimeMillis: 1800000
    numTestsPerEvictionRun: 3
    testOnCreate: false
    testOnBorrow: false
    testOnReturn: false
    testWhileIdle: false
    timeBetweenEvictionRunsMillis: -1
    blockWhenExhausted: true
    timeOut: 1000

metrics:
  metricsName: prometheus
  host:
  port: 9091
  async: true
  threadCount : 16
  jmxConfig:

spring配置说一下与hmily有关的配置:

  • 如果服务部署了几个节点, 负载均衡算法最好使用 hmily自带, 这样 tryconfirmcancel 调用会落在同一个节点 充分利用了缓存,提搞了效率。在你的yaml配置如下:
hmily:
  ribbon:
    rule:
      enabled: true
  • 如果用户配置了feign.hystrix.enabled = true, 默认使用线程池模式, 将会开启 HmilyHystrixConcurrencyStrategy 它在hystrix使用线程池模式的时候,能够照样通过threadLoacl 进行RPC传参数。
  • 需要进行分布式事务的SpringCloud微服务的调用方需要设置不重试:
#Ribbon的负载均衡策略
ribbon:
  NFLoadBalancerRuleClassName:  com.netflix.loadbalancer.RandomRule
  ConnectTimeout: 600 # 设置连接超时时间 default 2000
  ReadTimeout: 6000    # 设置读取超时时间  default 5000
  OkToRetryOnAllOperations: fasle # 对所有操作请求都进行重试  default false
  MaxAutoRetriesNextServer: 0    # 切换实例的重试次数  default 1
  MaxAutoRetries: 0     # 对当前实例的重试次数 default 0

业务代码

本文demo示例是模拟一个银行转账的场景,用户a转账给用户b,转账过程通过feign远程调用。

  1. 调用account-server1接口,传入转账金额,扣除用户a账户
  2. account-server1远程调用account-server2,给用户b账户加钱
  3. 自行解决空回滚、悬挂、幂等性问题(目前Hmily是已经完成了对空回滚、悬挂的自动处理,无需开发者自行处理,代码中已经自行处理,请忽略该部分处理的代码)

 

主要代码

转账服务account-server1(用户a侧服务)中:

    //全局事务id
    private String transId;

    // 账户扣款,就是tcc的try方法

    /**
     * 	try幂等校验
     * 	try悬挂处理
     * 	检查余额是够扣减金额
     * 	扣减金额
     * @param accountNo
     * @param amount
     */
    @Override
    @Transactional
    //只要标记@Hmily就是try方法,在注解中指定confirm、cancel两个方法的名字
    @HmilyTCC(confirmMethod="commit",cancelMethod="rollback")
    public void updateAccountBalance(String accountNo, Double amount) {
        //获取全局事务id
        transId = String.valueOf(HmilyContextHolder.get().getTransId());
        log.info("bank1 try begin 开始执行...xid:{}",transId);
        //幂等判断 判断local_try_log表中是否有try日志记录,如果有则不再执行
        if(accountMapper.isExistTry(transId)>0){
            log.info("bank1 try 已经执行,无需重复执行,xid:{}",transId);
            return ;
        }

        //try悬挂处理,如果cancel、confirm有一个已经执行了,try不再执行
        if(accountMapper.isExistConfirm(transId)>0 || accountMapper.isExistCancel(transId)>0){
            log.info("bank1 try悬挂处理  cancel或confirm已经执行,不允许执行try,xid:{}",transId);
            return ;
        }

        //扣减金额
        if(accountMapper.subtractAccountBalance(accountNo, amount)<=0){
            //扣减失败
            throw new RuntimeException("bank1 try 扣减金额失败,xid:{}"+transId);
        }
        //插入try执行记录,用于幂等判断
        accountMapper.addTry(transId);

        //远程调用李四,转账
        if(!account2Client.transfer(amount)){
            throw new RuntimeException("bank1 远程调用李四微服务失败,xid:{}"+transId);
        }
        if(amount == 2){
            throw new RuntimeException("人为制造异常,xid:{}"+transId);
        }
        log.info("bank1 try end 结束执行...xid:{}",transId);
    }

    //confirm方法
    @Transactional
    public void commit(String accountNo, Double amount){
        //获取全局事务id
//        String transId = String.valueOf(HmilyContextHolder.get().getTransId());
        log.info("bank1 confirm begin 开始执行...xid:{},accountNo:{},amount:{}",transId,accountNo,amount);
    }



    /** cancel方法
     * 	cancel幂等校验
     * 	cancel空回滚处理
     * 	增加可用余额
     * @param accountNo
     * @param amount
     */
    @Transactional
    public void rollback(String accountNo, Double amount){
        //获取全局事务id
//        String transId = String.valueOf(HmilyContextHolder.get().getTransId());
        log.info("bank1 cancel begin 开始执行...xid:{}",transId);
        //	cancel幂等校验
        if(accountMapper.isExistCancel(transId)>0){
            log.info("bank1 cancel 已经执行,无需重复执行,xid:{}",transId);
            return ;
        }
        //cancel空回滚处理,如果try没有执行,cancel不允许执行
        if(accountMapper.isExistTry(transId)<=0){
            log.info("bank1 空回滚处理,try没有执行,不允许cancel执行,xid:{}",transId);
            return ;
        }
        //	增加可用余额
        accountMapper.addAccountBalance(accountNo,amount);
        //插入一条cancel的执行记录
        accountMapper.addCancel(transId);
        log.info("bank1 cancel end 结束执行...xid:{}",transId);

    }

转账服务account-server2(用户b侧服务)中:

    private String transId;

    @Override
    @HmilyTCC(confirmMethod="confirmMethod", cancelMethod="cancelMethod")
    public void updateAccountBalance(String accountNo, Double amount) {
        //获取全局事务id
        transId = String.valueOf(HmilyContextHolder.get().getTransId());
        log.info("bank2 try begin 开始执行...xid:{}",transId);
    }

    /**
     * confirm方法
     * 	confirm幂等校验
     * 	正式增加金额
     * @param accountNo
     * @param amount
     */
    @Transactional
    public void confirmMethod(String accountNo, Double amount){
        //获取全局事务id
//        String transId = String.valueOf(HmilyContextHolder.get().getTransId());
        log.info("bank2 confirm begin 开始执行...xid:{}",transId);
        if(accountMapper.isExistConfirm(transId)>0){
            log.info("bank2 confirm 已经执行,无需重复执行...xid:{}",transId);
            return ;
        }

        //模拟运行时异常,触发cancel操作回滚
//        Integer.valueOf("QWQEQW");

        //增加金额
        accountMapper.addAccountBalance(accountNo,amount);
        //增加一条confirm日志,用于幂等
        accountMapper.addConfirm(transId);
        log.info("bank2 confirm end 结束执行...xid:{}",transId);
    }



    /**
     * @param accountNo
     * @param amount
     */
    public void cancelMethod(String accountNo, Double amount){
        //获取全局事务id
//        String transId = String.valueOf(HmilyContextHolder.get().getTransId());
        log.info("bank2 cancel begin 开始执行...xid:{}",transId);

    }

需要注意的是:

  • Hmily的全局事务开启于@HmilyTCC(confirmMethod = “confirm”, cancelMethod = “cancel”)注解的方法,confirmMethod指向confirm操作对应的方法,cancelMethod指向cancel操作对应的方法
  • 在服务被调用方的@FeignClient 接口方法上加上 @Hmily注解,就可以通过RPC调用传播事务
  • tryconfirmcancel 方法的所有异常不要自行catch 任何异常都应该抛出给 Hmily框架处理
  • 消费者feign请求的本地接口如果存在fallback方法的话,要么保证fallback也会出现异常,要么不要fallback,否则异常无法被Hmily捕获

 

 

 

 

 

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/145851.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • PS日记一

    PS日记一

  • 史上最全的Android面试题集锦

    史上最全的Android面试题集锦Android基本知识点1、常规知识点1、Android类加载器在Android开发中,不管是插件化还是组件化,都是基于Android系统的类加载器ClassLoader来设计的。只不过Android平台上虚拟机运行的是Dex字节码,一种对class文件优化的产物,传统Class文件是一个Java源码文件会生成一个.class文件,而Android是把所有Class文件进行合并、优化,然后…

  • 全球单片机的主要厂商和主要型号介绍图_51单片机包括哪些型号

    全球单片机的主要厂商和主要型号介绍图_51单片机包括哪些型号
    全球单片机的主要厂商和主要型号介绍
    PIC单片机: 
      是MICROCHIP公司的产品,其突出的特点是体积小,功耗低,精简指令集,抗干扰性好,可靠性高,有较强的模拟接口,代码保密性好,大部分芯片有其兼容的FLASH程序存储器的芯片. 
    EMC单片机:
      是台湾义隆公司的产品,有很大一部分与PIC 8位单片机兼容,且相兼容产品的资源相对比PIC的多,价格便宜,有很多系列可选,但抗干扰较差. 
    ATMEL单片机(51单片机):
    ATME

    2022年10月19日
  • G1垃圾收集器详解

    G1垃圾收集器详解G1垃圾收集器GC收集器的三个考量指标:占用的内存(Capacity)延迟(Latency)吞吐量(Throughput)随着硬件的成本越来越低,机器的内存也越来越大,GC收集器占用的内存基本上可以容忍。而吞吐量可以通过集群(增加机器)来解决。随着JVM中内存的增大,STW的时间成为JVM急迫解决的问题,如果还是按照传统的分代模型,使用传统的垃圾收集器,那么STW的时间将会越来越长。在传统的垃圾收集器中,STW的时间是无法预测的,有没有一种办法,能够首先定义一个停顿时间,然后反向推算收集内

  • Server unexpectedly closed network connection的解决

    Server unexpectedly closed network connection的解决(1)apt-getremoveopenssh-server(2)sudoaptinstallopenssh-server(3)sudoservicesshstart(4)ps-aux|grepssh(5)sudoaptinstallopenssh-client(6)工具重连…

    2022年10月21日
  • vi中跳到文件的第一行和最后一行

    vi中跳到文件的第一行和最后一行

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号