前言:写这个文章主要是给自己做下笔记以免遗忘,同时也给各大码农分享一下,互相学习。
分布式事务的需求来源于系统的服务化。在微服务系统中,无法使用传统的事务达到数据库的一致性。每个子服务都有自己独立的数据源,如果系统初期,没有分表分库,每个子服务的数据源都是连接同一个数据库主机,同一个数据库,那么还可以简单的利用传统的事务,增加代码的冗余达到事务效果。而往往如果已经需要考虑事务的时候,我们的系统应该已经达到了一定的用户量,开始了分表分库。故而分布式事务还是需要了解和使用的。
分布式事务有很多种实现方式,我在这主要谈论的是我熟悉的 “分三阶段提交模式”,这种模式的原理比较简单,当然了,性能上是有缺陷的。其他实现方式,可自行Google或者百度一下。
框架的特点
源码目录说明
不说太多了,直接上干货。(本文主要针对springcloud的)
1、去github.com下载tx-lcn-master源码
tx-lcn-master
目前更新到了4.1.0版本
LCN分布式事务框架的核心功能是对本地事务的协调控制,框架本身并不创建事务,只是对本地事务做协调控制。因此该框架与其他第三方的框架兼容性强,支持所有的关系型数据库事务,支持多数据源,支持与第三方数据库框架一块使用(例如 sharding-jdbc),在使用框架的时候只需要添加分布式事务的注解即可,对业务的侵入性低。LCN框架主要是为微服务框架提供分布式事务的支持,在微服务框架上做了进一步的事务机制优化,在一些负载场景上LCN事务机制要比本地事务机制的性能更好,4.0以后框架开方了插件机制可以让更多的第三方框架支持进来。
更多详情请参考官网 https://www.txlcn.org
我处理过的tx-lcn-master:https://pan.baidu.com/s/1M9SeulYe1j4ho9uuncWuBA
提取码:3ss3
2、配置分布式事务协调器TXmanager
把TXmanager配置好(redis连接信息,eureka注册中心,开放的端口),打包部署到自己的微服务中
主要修改的有三个地方:
#服务端口
server.port=8899
#eureka 地址
eureka.client.service-url.defaultZone=http://192.168.0.150:8761/eureka/
eureka.instance.prefer-ip-address=true
##redis 单点环境配置
#redis
#redis主机地址
spring.redis.host=127.0.0.1
#redis主机端口
spring.redis.port=6379
#redis链接密码
spring.redis.password=
spring.redis.pool.maxActive=10
spring.redis.pool.maxWait=-1
spring.redis.pool.maxIdle=5
spring.redis.pool.minIdle=0
spring.redis.timeout=0
eureka注册中心一定要配置自己系统的地址,关于TXmanager的运行机制在此不做太多的说明。
配置好了,打包发布即可进入被调用的状态。
3、服务中加入分布式事务管理(加入LCN事务管理)
1)把LCN框架源码编译安装到本地仓库
由于LCN的maven中央仓库不一定能下载到依赖,最好是自行把源码安装到本地仓库进行依赖。本人是这么做的。当然你也可以先尝试直接获取中央仓库的,如若不行再说。
2)pom.xml中添加LCN框架的依赖
4.1.0
com.codingapi
transaction-springcloud
${lcn.last.version}
org.slf4j
*
com.codingapi
tx-plugins-db
${lcn.last.version}
org.slf4j
*
transaction-springcloud LCN springcloud rpc框架扩展支持,我们这里使用的是springcloud
tx-plugins-db 是LCN 对关系型数据库的插件支持,我们这里使用的是mysql数据库
3)配置TXmanager的访问地址和端口号
spring.application.name = demo1
server.port = 8081
#${random.int[9000,9999]}
eureka.client.service-url.defaultZone=http://192.168.0.150:8761/eureka/
#txmanager地址
tm.manager.url=http://127.0.0.1:8899/tx/manager/
这里配置的txmanager地址就是我们在上面第二步配置的配置分布式事务协调器TXmanager并且部署到微服务环境中的子服务地址。我们发现,它还是使用传统的ip+port去访问的。虽然txmanager也作为子服务注册到了eureka上,但目前我还不确定是否可以根据applicationName访问到,读者可以试试。
4)分布式事务发起方
需要实现TxManagerTxUrlService和TxManagerHttpRequestService这两个接口,并作为bean注入到spring中。处理http请求和对服务器的连接
实现 TxManagerTxUrlService
import com.codingapi.tx.config.service.TxManagerTxUrlService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* create by lorne on 2018/11/18
*/
@Service
public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService{
@Value("${tm.manager.url}") //分布式事务协调器TXmanager访问地址
private String url;
@Override
public String getTxUrl() {
System.out.println("load tm.manager.url ");
return url;
}
}
实现 TxManagerHttpRequestService
import com.codingapi.tx.netty.service.TxManagerHttpRequestService;
import com.lorne.core.framework.utils.http.HttpUtils;
import org.springframework.stereotype.Service;
/**
* create by lorne on 2018/11/18
*/
@Service
public class TxManagerHttpRequestServiceImpl implements TxManagerHttpRequestService{
@Override
public String httpGet(String url) {
System.out.println("httpGet-start");
String res = HttpUtils.get(url);
System.out.println("httpGet-end");
return res;
}
@Override
public String httpPost(String url, String params) {
System.out.println("httpPost-start");
String res = HttpUtils.post(url,params);
System.out.println("httpPost-end");
return res;
}
}
加入分布式事务注解@TxTransaction(isStart = true),开启分布式事务。isStart = true声明为分布式事务发起方
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
private Demo2Feign demo2Feign;//远程调用,同一数据库,有事务
@Autowired
private Demo3Feign demo3Feign;//远程调用,不同数据库,有事务
@Autowired
private TestMapper testMapper;
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public List list() {
return testMapper.findAll();
}
@Override
@TxTransaction(isStart = true)
@Transactional
public int save() {
int rs1 = testMapper.save("mybatis-hello-1", new Date());
logger.info("本地服务数据插入成功");
int rs2 = demo2Feign.save();
logger.info("远程服务2号数据插入成功");
int rs3 = demo3Feign.save();
logger.info("远程服务3号数据插入成功");
logger.info("插入" + (rs1 + rs2 + rs3) + "条记录");
logger.info("制造异常");
int v = 100 / 0;
return rs1 + rs2 + rs3;
}
}
如上代码执行完成以后所有参与此分布式事务模块都将回滚事务。
5)分布式事务被调用方
需要实现TxManagerTxUrlService个接口,并作为bean注入到spring中。处理对服务器的连接
import com.codingapi.tx.config.service.TxManagerTxUrlService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* create by lorne on 2018/11/18
*/
@Service
public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService{
@Value("${tm.manager.url}") //分布式事务协调器TXmanager访问地址
private String url;
@Override
public String getTxUrl() {
System.out.println("load tm.manager.url ");
return url;
}
}
加入分布式事务注解@TxTransaction,或者实现ITxTransaction接口,开启分布式事务。等待起调方发起事务
@Service
public class DemoServiceImpl implements DemoService, ITxTransaction {
@Autowired
private TestMapper testMapper;
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public List list() {
return testMapper.findAll();
}
@Override
@Transactional
public int save() {
int rs = testMapper.save("mybatis-hello-2", new Date());
logger.info("插入" + rs + "条记录");
return rs;
}
}
6)说明:在使用LCN分布式事务时,只需要将事务的开始方法添加@TxTransaction(isStart=true)注解即可,在参与方添加@TxTransaction或者实现ITxTransaction接口即可。详细查看官网说明
7)demo
https://pan.baidu.com/s/1IaTilRxmFQBNj9H0W6BFzg
提取码:979b