前言
之前我写过一个基于 springboot1.5.6+lcn4.1.0 的集成文章 https://segmentfault.com/a/11…,基于目前更新的 lcn5.0.2 分析了一波源码。在此做一些记录(ps: 如果对分布式事务不是很了解,可以先看下我上面贴的链接,本文基于有基础的情况去分析的)
TX-LCN 的 3 种模式
LCN5.0.2 有 3 种模式,分别是 LCN 模式,TCC 模式,TXC 模式
-
LCN 模式:
LCN 模式是通过代理 Connection 的方式实现对本地事务的操作,然后在由 TxManager 统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由 LCN 连接池管理。
该模式的特点:- 该模式对代码的嵌入性为低。- 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。- 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。- 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。
-
TCC 模式:
TCC 事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器 (RM) 对 XA 的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、Confirm: 确认执行业务、Cancel: 取消执行业务。
该模式的特点:
- 该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。- 该模式对有无本地事务控制都可以支持使用面广。- 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。
-
TXC 模式:
TXC 模式命名来源于淘宝,实现原理是在执行 SQL 之前,先查询 SQL 的影响数据,然后保存执行的 SQL 快走信息和创建锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖 redis 分布式锁控制。
该模式的特点:- 该模式同样对代码的嵌入性低。- 该模式仅限于对支持 SQL 方式的模块支持。- 该模式由于每次执行 SQL 之前需要先查询影响数据,因此相比 LCN 模式消耗资源与时间要多。- 该模式不会占用数据库的连接资源。
LCN 原理
这里引用一下官网的原理图:
总的来说,核心步骤就是创建事务组,假如事务组,通知事务组
创建事务组
- 是指在事务发起方开始执行业务代码之前先调用 TxManager 创建事务组对象,然后拿到事务标示 GroupId 的过程。
加入事务组
- 添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给 TxManager 的操作
通知事务组
- 是指在发起方执行完业务代码以后,将发起方执行结果状态通知给 TxManager,TxManager 将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。
集成 springCloud
(ps:我这里使用的 springboot 版本是 2.1.3)
<!----pom.xml 今天是 2019.4.24 最新版本是 5.0.2 --->
<properties>
<codingapi.txlcn.version>5.0.2.RELEASE</codingapi.txlcn.version>
</properties>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
<version>${codingapi.txlcn.version}</version>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tm</artifactId>
<version>${codingapi.txlcn.version}</version>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
<version>${codingapi.txlcn.version}</version>
</dependency>
A 启动类上加注解:@EnableDistributedTransaction 表明这是一个 txmanager 的 client
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableDistributedTransaction
@MapperScan("cn.iamcrawler.crawlergoddess.mapper")
@ComponentScan("cn.iamcrawler.crawlergoddess,cn.iamcrawler.crawler_common.feign")
public class CrawlerGoddessApplication {public static void main(String[] args) {SpringApplication.run(CrawlerGoddessApplication.class, args);
}
}
yml(ps 我把密码隐藏了, 数据库我用的是 postgresql,lcn 默认的是 mysql):
server:
port: 9993
spring:
datasource:
url: jdbc:postgresql://www.iamcrawler.cn:5432/mft?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC
username: postgres
password: **
driver-class-name: org.postgresql.Driver
redis:
host: www.iamcrawler.cn
port: 6379
password: **
database: 0
application:
name: crawler-goddess
rabbitmq:
host: www.iamcrawler.cn
port: 5672
username: liuliang
password: **
jpa:
properties:
hibernate:
jdbc:
lob:
non_contextual_creation: true
database-platform: org.hibernate.dialect.PostgreSQL9Dialect
# aop:
# proxy-target-class: true
eureka:
client:
service-url:
defaultZone: http://localhost:9990/eureka/
init-db: true
tx-lcn:
client:
manager-address: 127.0.0.1:8070
logger:
enabled: true
driver-class-name: ${spring.datasource.driver-class-name}
jdbc-url: jdbc:postgresql://www.iamcrawler.cn:5432/mft?characterEncoding=UTF-8
username: ${spring.datasource.username}
password: ${spring.datasource.password}
logging:
level:
com:
codingapi: debug
A 模块代码示例:
@Transactional
@LcnTransaction// 表明使用 lcn 模式
public void testLcn(){PgTestUser testUser = new PgTestUser();
testUser.setAge(1);
testUser.setName("liuliang");
testUser.setRemark("要买东西");
testUser.setSalary(new BigDecimal(10));
this.save(testUser);
PgOrder order = new PgOrder();
order.setAmount(new BigDecimal(10));
order.setItemName("小视频");
kindnessFeign.insertOne(order);
logger.info("===============");
throw new RuntimeException("");
// logger.info("插入完毕... 事务提交!");
}
B 模块代码示例
@LcnTransaction
@Transactional
public Boolean insertOne(PgOrder pgOrder) {RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = requestAttributes == null ? null : ((ServletRequestAttributes) requestAttributes).getRequest();
// String groupId = TracingContext.tracing().groupId();
// String applicationId = Transactions.getApplicationId();
boolean save = pgOrderService.save(pgOrder);
return save;
}
然后调用即可,LCN5.0.2 的 lcn 模式也 4.1.0 的 TX 模式很类似,就是不用标记 isStart=true 了,并且还支持 TCC 模式和 TXC 模式,TCC 模式可以控制多个连接,比如 A 使用 mysql,B 使用 Redis,C 使用 Mongodb,D 模块使用 postgresql,可以使用 TCC 模式。但是 TCC 模式需要有注意的地方。下一篇文章将结合源码仔细说明