• spring boot 分布式事务解决方案LCN


    对比LCN和saga(华为apache孵化器项目) ,LCN使用代理连接池封装补偿方法,saga需要手工写补偿方法,相对来说LCN使用更加方便。

    参考官方地址:

    https://github.com/codingapi/tx-lcn/wiki/TxManager%E5%90%AF%E5%8A%A8%E8%AF%B4%E6%98%8E

    1.    原理

    1.     事务控制原理

    LCN事务控制原理是由事务模块TxClient下的代理连接池与TxManager的协调配合完成的事务协调控制。

    TxClient的代理连接池实现了javax.sql.DataSource接口,并重写了close方法,事务模块在提交关闭以后TxClient连接池将执行"假关闭"操作,等待TxManager协调完成事务以后在关闭连接。

    2.     调用时序图

    1.     正常

    2.     异常

    2.    服务端

    tx-manager 4.1.0

    3.    客户端

    1.     pom添加依赖

        <properties>

            <lcn.last.version>4.1.0</lcn.last.version>

        </properties>

            <dependency>

                <groupId>org.mybatis.spring.boot</groupId>

                <artifactId>mybatis-spring-boot-starter</artifactId>

                <version>1.1.1</version>

            </dependency>

            <dependency>

                <groupId>com.codingapi</groupId>

                <artifactId>transaction-springcloud</artifactId>

                <version>${lcn.last.version}</version>

                <exclusions>

                    <exclusion>

                       <groupId>org.slf4j</groupId>

                       <artifactId>*</artifactId>

                    </exclusion>

                </exclusions>

            </dependency>

            <dependency>

                <groupId>com.codingapi</groupId>

                <artifactId>tx-plugins-db</artifactId>

                <version>${lcn.last.version}</version>

                <exclusions>

                    <exclusion>

                       <groupId>org.slf4j</groupId>

                       <artifactId>*</artifactId>

                    </exclusion>

                </exclusions>

            </dependency>

    2.    配置文件

    #Ribbon的负载均衡策略:随机

    #ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

    #由于springcloud默认是开启的重试机制,开启次机制以后会导致当springcloud请求超时时会重复调用业务模块,从而会引发数据混乱,因此建议将其禁用。对于网络模块超时等故障问题建议使用hytrix方式。

    #ribbon.MaxAutoRetriesNextServer=0

     

    tm:

      manager:

        url: http://localhost:8899/tx/manager/

    ribbon:

      NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

      MaxAutoRetriesNextServer: 0

    init-db:true

    hystrix:

      command:

        default:

          execution:

            isolation:

              thread:

                timeoutInMilliseconds: 6000

    3.     Service包下处理http请求和对服务器的连接

    package com.svw.tbox.tcloud.commons.ms.service;

     

    import com.codingapi.tx.netty.service.TxManagerHttpRequestService;

    import com.lorne.core.framework.utils.http.HttpUtils;

    import org.springframework.stereotype.Service;

     

    @Service

    publicclass TxManagerHttpRequestServiceImpl implements TxManagerHttpRequestService{

     

        @Override

        public String httpGet(String url) {

        //GET请求前

            String res = HttpUtils.get(url);

            //GET请求后

            returnres;

        }

     

        @Override

        public String httpPost(String url, String params) {

        //POST请求前

            String res = HttpUtils.post(url,params);

            //POST请求后

            returnres;

        }

    }

    package com.svw.tbox.tcloud.commons.ms.service;

     

    import com.codingapi.tx.config.service.TxManagerTxUrlService;

    import org.springframework.beans.factory.annotation.Value;

    import org.springframework.stereotype.Service;

     

    @Service

    public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService{

     

        @Value("${tm.manager.url}")

        private String url;

     

        @Override

        public String getTxUrl() {

        //load tm.manager.url

            return url;

        }

    }

    4.    启动类配置代理连接池

    import javax.sql.DataSource;

    import org.mybatis.spring.annotation.MapperScan;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.boot.SpringApplication;

    import org.springframework.boot.autoconfigure.SpringBootApplication;

    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

    import org.springframework.cloud.netflix.hystrix.EnableHystrix;

    import org.springframework.context.annotation.Bean;

    import org.springframework.context.annotation.ComponentScan;

    import org.springframework.core.env.Environment;

    import com.alibaba.druid.pool.DruidDataSource;

     

    @SpringBootApplication

    @EnableDiscoveryClient

    @EnableHystrix

    @MapperScan(basePackages = "com.svw.tbox.tcloud.commons.ms.dao")

    @ComponentScan(basePackages = { "com.svw.tbox.tcloud" })

    publicclass MsApplication {

        ……

    @Autowired

        private Environment env;

     

        @Bean

        public DataSource dataSource() {

            DruidDataSource dataSource = new DruidDataSource();

            dataSource.setUrl(env.getProperty("spring.datasource.url"));

            dataSource.setUsername(env.getProperty("spring.datasource.username"));//用户名

            dataSource.setPassword(env.getProperty("spring.datasource.password"));//密码

            dataSource.setInitialSize(2);

            dataSource.setMaxActive(20);

            dataSource.setMinIdle(0);

            dataSource.setMaxWait(60000);

            dataSource.setValidationQuery("SELECT 1");

            dataSource.setTestOnBorrow(false);

            dataSource.setTestWhileIdle(true);

            dataSource.setPoolPreparedStatements(false);

            returndataSource;

        }

    5.    测试代码

    调用方tcloud-mds => 参与方tcloud-commons

    1.     调用方:tcloud-mds

    package com.svw.tbox.tcloud.commons.api.feign;

     

    import org.springframework.cloud.netflix.feign.FeignClient;

    import com.svw.tbox.tcloud.commons.api.config.TxFeignConfiguration;

    import com.svw.tbox.tcloud.commons.api.service.SysErrorCodeMappingService;

     

    /**

     * <p>ClassName: SysErrorCodeMappingFeign</p>

     * <p>Description: 远程调用错误码服务</p>

     * <p>Author: hurf</p>

     * <p>Date: 2017年12月11日</p>

     */

    @FeignClient(value = "tcloud-commons-ms")

    publicinterface SysErrorCodeMappingFeign extends SysErrorCodeMappingService {

    }

    2.     事务发起@TxTransaction(isStart=true)

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.stereotype.Service;

    import org.springframework.transaction.annotation.Transactional;

     

    import com.codingapi.tx.annotation.TxTransaction;

    import com.svw.tbox.tcloud.commons.api.entity.SysErrorCodeMapping;

    import com.svw.tbox.tcloud.commons.api.feign.SysErrorCodeMappingFeign;

    import com.svw.tbox.tcloud.commons.api.service.CmnService;

    import com.svw.tbox.tcloud.commons.api.service.JedisTemplate;

    import com.svw.tbox.tcloud.commons.util.DateUtil;

    import com.svw.tbox.tcloud.mds.entity.ThUserLogin;

     

    /**

     * @Title<p>ClassName: UserTokenService</p>

     * @Description<p>Description: 登录服务</p>

     * @Author<p>Author: hurf</p>

     * @Date<p>Date: 2018年2月6日</p>

     */

    @Service

    publicclass UserTokenService extends CmnService<ThUserLogin>{

        @Autowired

        private JedisTemplate jedisTemplate;

       

        @Autowired

        private SysErrorCodeMappingFeign sysErrorCodeMappingFeign;

       

        @Transactional

        @TxTransaction(isStart=true)

        public String add(SysErrorCodeMapping sysErrorCodeMapping) {

            // 远程调用新增

            sysErrorCodeMappingFeign.add(sysErrorCodeMapping);

            // 本地新增db

            insertSelective(ThUserLogin.builder().accessToken(sysErrorCodeMapping.getApiCode())

                    .refreshToken(sysErrorCodeMapping.getInnerErrorCode()).createBy("测试事务").build());

            //本地缓存事务

            jedisTemplate.set("isStart", DateUtil.getNow());

    //      int ii = 1/0;//异常

           

            return"测试分布式事务成功";

        }

    }

    3.     事务参与方tcloud-commons-ms: @Transactional

    @RestController

    publicclass SysErrorCodeMappingController implements SysErrorCodeMappingService {

     

        @Autowired

        private MsService msService;

     

        @ApiOperation("添加错误码信息")

        @Override

        public SystemResponse add(@RequestBody SysErrorCodeMapping sysErrorCodeMapping) {

            returnmsService.add(sysErrorCodeMapping);

        }

        。。。。。。

    importcom.codingapi.tx.annotation.ITxTransaction;

     

    @Service

    @CacheConfig(cacheNames = "sys-code-resource")

    publicclass MsService implements ITxTransaction{

     

        @Autowired

        private JedisTemplate jedisTemplate;

     

        @Autowired

        private SysErrorCodeMappingMapper sysErrorCodeMappingMapper;

        /**

         * <p>Title: 事务参与方</p>

         * <p>Description: </p>

         * @param sysErrorCodeMapping

         * @return

         */

        @Transactional

        public SystemResponse add(SysErrorCodeMapping sysErrorCodeMapping) {

            //db操作

            sysErrorCodeMapping.setVersion(1);

            sysErrorCodeMapping.setDelFlag(Short.valueOf("0"));

            sysErrorCodeMapping.setCreatedBy("admin");

            sysErrorCodeMapping.setCreateDate(new Date());

            sysErrorCodeMappingMapper.insertSelective(sysErrorCodeMapping);

           

            //redis操作

            jedisTemplate.set("addTest"+DateUtil.getNow(),"tttttttttttttttttttttt");

            return ResultUtil.success(refreshAll());

        }

    6.     效果

    启动两个微服务,访问调用方接口

    1.     正常情况

    2.     异常回滚情况

    删除刚刚的测试数据,开启异常情况:

        @Transactional

        @TxTransaction(isStart=true)

        public String add(SysErrorCodeMapping sysErrorCodeMapping) {

            // 远程调用新增

            sysErrorCodeMappingFeign.add(sysErrorCodeMapping);

            // 本地新增db

            insertSelective(ThUserLogin.builder().accessToken(sysErrorCodeMapping.getApiCode())

                    .refreshToken(sysErrorCodeMapping.getInnerErrorCode()).createBy("测试事务").build());

            //本地缓存事务

            jedisTemplate.set("isStart", DateUtil.getNow());

            intii = 1/0;//异常

           

            return"测试分布式事务成功";

        }

     

    发现mysql已经回滚了,但是redis没有回滚 =》 目前只支持db分布式事务。

  • 相关阅读:
    【字符串题目】poj 3096 Surprising Strings
    【转载】:【博弈论】博弈的学习和总结
    【博弈论】hihocoder
    转载:SPFA算法学习
    马克思所言:
    【NOIP2013】火柴排队
    【NOIP2013】【P1441】花匠
    【JZOI2002】【BZOJ1477】【P1371】青蛙的约会
    【P1373】奶牛的卧室
    2016.9.16 の 測試
  • 原文地址:https://www.cnblogs.com/dousnl/p/9772632.html
Copyright © 2020-2023  润新知