spring boot 分佈式事務解決方案LCN

對比LCN和saga(華爲apache孵化器項目) ,LCN使用代理鏈接池封裝補償方法,saga須要手工寫補償方法,相對來講LCN使用更加方便。java

 

參考官方地址:mysql

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

1.    原理

1.     事務控制原理

LCN事務控制原理是由事務模塊TxClient下的代理鏈接池與TxManager的協調配合完成的事務協調控制。github

TxClient的代理鏈接池實現了javax.sql.DataSource接口,並重寫了close方法,事務模塊在提交關閉之後TxClient鏈接池將執行"假關閉"操做,等待TxManager協調完成事務之後在關閉鏈接。redis

2.     調用時序圖

1.     正常

2.     異常

2.    服務端

tx-manager 4.1.0spring

3.    客戶端

1.     pom添加依賴

    <properties>sql

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

    </properties>api

        <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分佈式事務。

相關文章
相關標籤/搜索