1.概述
tx-lcn 官方地址:https://www.txlcn.org/
tx-lcn Github地址:https://github.com/codingapi/tx-lcn
TX-LCN定位于一款事务协调性框架,框架其本身并不操作事务,而是基于对事务的协调从而达到事务一致性的效果。
在一个分布式系统下存在多个模块协调来完成一次业务。那么就存在一次业务事务下可能横跨多种数据源节点的可能。TX-LCN将可以解决这样的问题。
TX-LCN由两大模块组成, TxClient、TxManager,TxClient作为模块的依赖框架,提供TX-LCN的标准支持,TxManager作为分布式事务的控制放。事务发起方或者参与反都由TxClient端来控制。
原理图:
核心步骤
-
创建事务组
是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。 -
加入事务组
添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给TxManager的操作。 -
通知事务组
是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager,TxManager将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。
更详细的介绍见官网,这里只说明下基础环境的搭建,以及搭建和测试过程中遇到的一些坑,本文以官网最新版本5.0.2为例进行说明。
2.开发环境搭建
tx-manager环境
tx-manager有两种方式,一种是直接去github上下载源码,并更新源码的配置文件,最后打成jar包进行运行;也可以自己创建一个springboot项目,依赖lcn相关依赖,如下所示:
<!--1.pom文件中添加依赖-->
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tm</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--2.启动类上加上注解EnableTransactionManagerServer-->
@SpringBootApplication
@EnableTransactionManagerServer
public class TransactionManagerApplication {
public static void main(String[] args) {
SpringApplication.run(TransactionManagerApplication.class, args);
}
}
<!--3.添加spring配置文件,配置项可以去官网复制并修改-->
<!--4.启动tx-manager,然后可以在浏览器中输入http://localhost:7970/admin/index.html#/login进入tx-manager后台管理页面,密码默认为codingapi-->
springcloude微服务环境
<!--1.pom文件中添加依赖-->
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--2.启动类上加上注解EnableDistributedTransaction-->
@SpringBootApplication
@EnableDistributedTransaction
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
<!--3.tx-manager配置--->
tx-lcn.client.manager-address=127.0.0.1:8070
<!--4.业务代码如下-->
<!--4.1 订单系统业务代码如下-->
@RestController
public class OrderController {
@Resource
private OrderServiceImpl orderService;
@RequestMapping(value = "/addOrder", method = RequestMethod.POST)
public void addOrder(@RequestBody Order order){
orderService.saves();
}
}
@Service
public class OrderServiceImpl {
@Resource
private OrderMapper orderMapper;
@Resource
private StockServiceApi stockServiceApi;
@LcnTransaction//lcn事务配置
@Transactional
public void saves(Order order){
OrderPo po=new OrderPo();
<!--其他属性设置-->
orderMapper.saves(po);//本地事务
stockServiceApi.update(po);//远程事务
int i=100/0;
}
}
@FeignClient( name = "stock-service")
public interface HostingInfoApi {
@RequestMapping(value = "/addStock", method = RequestMethod.POST)
public void addStock(@RequestBody Order order);
}
<!--4.2 库存系统业务代码如下-->
@RestController
public class StockController {
@Resource
private StockServiceImpl stockService;
@RequestMapping(value = "/addStock", method = RequestMethod.POST)
public void addStock(@RequestBody Order order){
stockService.update(order);
}
}
@Service
public class StockServiceImpl {
@Resource
private StockMapper stockMapper;
@LcnTransaction//lcn事务配置
@Transactional
public void saves(Order order){
stockMapper.update(order);
}
}
3.遇到的坑
3.1 如果按照上述方式搭建的springcloude微服务环境,是不会成功处理分布式事务的,还需要在每个微服务中添加如下代码:
@Component
@Aspect
public class MyDataSourceAspect implements Ordered {
@Autowired
private DTXResourceWeaver dtxResourceWeaver;//TX-LCN 资源切面处理对象
@Around("execution(public java.sql.Connection org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(..) )")
public Object around(ProceedingJoinPoint point) throws Throwable {
return dtxResourceWeaver.getConnection(() -> (Connection) point.proceed());
}
@Override
public int getOrder() {
return 0;
}
}
3.2 假设我在StockController中catch了异常,则对于OrderService而已,那么StockController就是一个成功的请求,即使StockService报异常且事务也将回滚。因此要么不catch异常,要么在调用端(OrderService)进行返回数据解析,根据解析结果确定是抛异常中断执行还是继续后面的执行。一般对应微服务而言,返回结果都不应该是异常,而是一个自定义的数据结构,如{"code":"100","message":"异常返回,异常原因....."},最好的方式是在调用端OrderService写一个拦截器进行拦截处理(FeignClient拦截器)。