LCN分布式事务实现
1、建库建表
1.1、库1:tx-manager
CREATE TABLE `t_tx_exception` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`transaction_state` tinyint(4) NULL DEFAULT NULL,
`registrar` tinyint(4) NULL DEFAULT NULL,
`remark` varchar(4096) NULL DEFAULT NULL,
`ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 未解决 1已解决',
`create_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
1.2、库2:测试业务数据txlcn-demo
create table `t_demo` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`kid` varchar(45) DEFAULT NULL,
`group_id` varchar(64) DEFAULT NULL,
`demo_field` varchar(255) DEFAULT NULL,
`app_name` varchar(128) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
2、项目搭建
2.1、源码Demo
https://github.com/codingapi/txlcn-demo
2.2、项目结构
2.2.1、说明:事务的注解
- LCN事务模式
- 该模式对代码的嵌入性为低。
- 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
- 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
- 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。
- TCC事务模式
- 该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
- Try: 尝试执行业务
- Cancel: 取消执行业务,单独定义一个方法,事务取消时执行
- Confirm:确认执行业务,单独定义一个方法,事务确认时执行
- 该模式对有无本地事务控制都可以支持使用面广。
- 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。
- 该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
- TXC事务模式
- 该模式同样对代码的嵌入性低。
- 该模式仅限于对支持SQL方式的模块支持。
- 该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。
- 该模式不会占用数据库的连接资源。
2.3、代码实现
2.3.1、TM端
2.3.1.1、POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>txlcn-demo</artifactId>
<groupId>org.txlcn.demo</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>txlcn-demo-tm</artifactId>
<name>txlcn-demo-tm</name>
<dependencies>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tm</artifactId>
</dependency>
</dependencies>
</project>
2.3.1.2、application.properties
spring.application.name=TransactionManager
server.port=7970
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tx-manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=mysqladmin
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.use-generated-keys=true
2.3.1.3、主启动类
package org.txlcn.tm;
import com.codingapi.txlcn.tm.config.EnableTransactionManagerServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableTransactionManagerServer
public class TransactionManagerApplication {
public static void main(String[] args) {
SpringApplication.run(TransactionManagerApplication.class, args);
}
}
2.3.2、TC端
2.3.2.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.3.2.2、application.properties
# 默认之配置为TM的本机默认端口
tx-lcn.client.manager-address=127.0.0.1:8070
2.3.2.3、主启动类
@SpringBootApplication
@EnableDistributedTransaction
public class DemoAApplication {
public static void main(String[] args) {
SpringApplication.run(DemoDubboClientApplication.class, args);
}
}
2.3.2.4、接口
public interface DemoApiService {
String execute(String name, String exFlag);
}
2.3.2.5、接口实现类
package org.txlcn.demo.dubbo.servicea;
import com.alibaba.dubbo.config.annotation.Reference;
import com.codingapi.txlcn.common.util.Transactions;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.codingapi.txlcn.tracing.TracingContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.txlcn.demo.common.db.domain.Demo;
import org.txlcn.demo.common.dubbo.DemoServiceB;
import org.txlcn.demo.common.dubbo.DemoServiceC;
import java.util.Date;
import java.util.Objects;
@Service
public class DemoApiServiceImpl implements DemoApiService {
@Reference(version = "${demo.service.version}",
application = "${dubbo.application.b}",
registry = "${dubbo.registry.address}",
retries = -1,
check = false,
loadbalance = "txlcn_random")
private DemoServiceB demoServiceB;
@Reference(version = "${demo.service.version}",
application = "${dubbo.application.c}",
retries = -1,
check = false,
registry = "${dubbo.registry.address}",
loadbalance = "txlcn_random")
private DemoServiceC demoServiceC;
@Autowired
private DemoMapper demoMapper;
@Override
@LcnTransaction
public String execute(String name, String exFlag) {
String bResp = demoServiceB.rpc(name);
String cResp = demoServiceC.rpc(name);
Demo demo = new Demo();
demo.setGroupId(TracingContext.tracing().groupId());
demo.setDemoField(name);
demo.setAppName(Transactions.getApplicationId());
demo.setCreateTime(new Date());
demoMapper.save(demo);
int a = 10/0;
if (Objects.nonNull(exFlag)) {
throw new IllegalStateException("by exFlag");
}
return bResp + " > " + cResp + " > " + "ok-service-a";
}
}
2.3.2.6、Controller
package org.txlcn.demo.dubbo.servicea;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoConsumerController {
@Autowired
private DemoApiService demoApiService;
@RequestMapping("/txlcn")
public String sayHello(@RequestParam("value") String value,
@RequestParam(value = "ex", required = false) String exFlag) {
return demoApiService.execute(value, exFlag);
}
}
2.3.3、测试
访问:http://localhost:12004/txlcn?value=1
因为有int a = 10/0;必然报错,此时数据库没有数据就对了
去掉这个异常,可以插入,测试通过