對比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
LCN事務控制原理是由事務模塊TxClient下的代理鏈接池與TxManager的協調配合完成的事務協調控制。github
TxClient的代理鏈接池實現了javax.sql.DataSource接口,並重寫了close方法,事務模塊在提交關閉之後TxClient鏈接池將執行"假關閉"操做,等待TxManager協調完成事務之後在關閉鏈接。redis
tx-manager 4.1.0spring
<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> |
#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 |
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; } } |
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; } |
調用方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 { } |
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"測試分佈式事務成功"; } } |
@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()); } |
啓動兩個微服務,訪問調用方接口
刪除剛剛的測試數據,開啓異常狀況:
@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分佈式事務。