转载

spring boot 分布式事务(LCN)

编辑推荐:

本文来自于 csdn,将从原理,调用时序图,客户端,启动类配置代理连接池,测试代码和效果这几个方面来阐述分布式事务解决方案LCN。

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

参考官方地址

1. 原理

1. 事务控制原理

spring boot 分布式事务(LCN)

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

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

2. 调用时序图

1. 正常

spring boot 分布式事务(LCN)

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

 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(),"ttttttttttt ttttttttttt");
 return ResultUtil.success(refreshAll());
 }
 

6. 效果

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

1. 正常情况

spring boot 分布式事务(LCN)

spring boot 分布式事务(LCN)

spring boot 分布式事务(LCN)

spring boot 分布式事务(LCN)

spring boot 分布式事务(LCN)

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"测试分布式事务成功";
 }

spring boot 分布式事务(LCN)

spring boot 分布式事务(LCN)

spring boot 分布式事务(LCN)

spring boot 分布式事务(LCN)

spring boot 分布式事务(LCN)

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

原文  http://www.uml.org.cn/zjjs/201909254.asp
正文到此结束
Loading...