Seata是一款开源的分布式解决方案,提供高性能和简单易用的额分布式事务服务。提供了AT、TCC、SAGA和XA事务模式
Seata事务管理中三个重要的角色:
1.TC(Transaction Coordinate)-事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚
2.TM(Transition Manager)-事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务
3.RM(Resource Manager)-资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚
Seata提供四种不同的分布式事务解决方案:
XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
TCC模式:最终一致的分阶段事务模式,有业务侵入
AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
SAGA模式:长事务模式,有业务侵入
解压Seata,修改registry.conf配置文件
1.选择自己要使用的配置中心(nacos)全部替换成nacos;
2.修改file.conf(存储模式)
Server的存储模式(mode)支持三种:
- file:(默认)单击模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高(默认)
- db:高可用模式,全局事务会话信息通过db共享,相应性能差些
- Redis:Seata-server1.3以及以上版本支持,性能较高,存在事务信息丢失风险,请提前配置合适当前场景的redis持久化配置资源目录
在数据库中创建数据库seata,,创建数据库表
CREATE TABLE `branch_table` (
`branch_id` bigint NOT NULL,
`xid` varchar(128) NOT NULL,
`transaction_id` bigint DEFAULT NULL,
`resource_group_id` varchar(32) DEFAULT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`branch_type` varchar(8) DEFAULT NULL,
`status` tinyint DEFAULT NULL,
`client_id` varchar(64) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime(6) DEFAULT NULL,
`gmt_modified` datetime(6) DEFAULT NULL,
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `distributed_lock` (
`lock_key` char(20) NOT NULL,
`lock_value` varchar(20) NOT NULL,
`expire` bigint DEFAULT NULL,
PRIMARY KEY (`lock_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `global_table` (
`xid` varchar(128) NOT NULL,
`transaction_id` bigint DEFAULT NULL,
`status` tinyint NOT NULL,
`application_id` varchar(32) DEFAULT NULL,
`transaction_service_group` varchar(32) DEFAULT NULL,
`transaction_name` varchar(128) DEFAULT NULL,
`timeout` int DEFAULT NULL,
`begin_time` bigint DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`xid`),
KEY `idx_status_gmt_modified` (`status`,`gmt_modified`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `lock_table` (
`row_key` varchar(128) NOT NULL,
`xid` varchar(128) DEFAULT NULL,
`transaction_id` bigint DEFAULT NULL,
`branch_id` bigint NOT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`table_name` varchar(32) DEFAULT NULL,
`pk` varchar(36) DEFAULT NULL,
`status` tinyint NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`row_key`),
KEY `idx_status` (`status`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
修改script-config-center文件中的config.txt,同样将存储模式改为db,修改具体信息;配置自己分组service.vgroup_mapping.my_test_tx_group=default改为service.vgroup_mapping.自己=default
将配置文件配置到nacos,使用git
即配置成功!
下面进行模拟分布式事务:分别创建两个数据库(假设)-order订单数据库-money支付表数据库,即对应的订单表和支付表。分别创建两个项目:支付和订单
模拟之前一定要在两个数据库中加入undo_log表
CREATE TABLE `undo_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`branch_id` bigint NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3
支付模块
server.port=8803
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url= jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.username= root
spring.datasource.password= 123456
mybatis.configuration.map-underscore-to-camel-case= true
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.application.name=money
spring.cloud.alibaba.seata.tx-service-group=guangzhou
#配置seata的注册中心,告诉seata client 怎么去访问seata servellr
seata.registry.type=nacos
#可不配服务名
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr= localhost:8848
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos
#seata server所在的分组,默认就是这个,没有可以不配
seata.registry.nacos.group=SEATA_GROUP
#配置seata的配置中心,
seata.config.type=nacos
seata.config.nacos.server-addr=localhost:8848
seata.config.nacos.username=nacos
seata.config.nacos.password=nacos
@Mapper
@Repository
public interface MoneyMapper {
public int subMoney();
}
@Service
public class MoneyService {
@Resource
private MoneyMapper moneyMapper;
public void subMoney(){
moneyMapper.subMoney();
}
}
@RestController
public class MoneyController {
@Resource
MoneyService moneyService;
@RequestMapping("/money")
public String money(){
moneyService.subMoney();
return "支付成功";
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.seatamoney.mapper.MoneyMapper">
<update id="subMoney">
update moneyTest set money=money-10 where id=1;
</update>
</mapper>
订单模块
server.port=8180
provider.ribbon.NFLoadBalancerRuleClassName:com.example.consumer.configuration.NacosWeightedRule
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url= jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.username= root
spring.datasource.password= 123456
mybatis.mapper-locations= classpath:com/example/consumer/mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case= true
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.application.name=consumer
spring.cloud.alibaba.seata.tx-service-group=guangzhou
#配置seata的注册中心,告诉seata client 怎么去访问seata server
seata.registry.type=nacos
#可不配服务名
seata.registry.nacos.application=seat-server
seata.registry.nacos.server-addr= localhost:8848
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos
#seata server所在的分组,默认就是这个,没有可以不配
seata.registry.nacos.group=SEATA_GROUP
#配置seata的配置中心,
seata.config.type=nacos
seata.config.nacos.server-addr=localhost:8848
seata.config.nacos.username=nacos
seata.config.nacos.password=nacos
openFeign接口
@Component
@FeignClient(value = "money")
public interface MoneyFeign {
@RequestMapping("/money")
public String money();
}
@Repository
@Mapper
public interface OrderMapper {
public int addOrder();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.consumer.mapper.OrderMapper">
<update id="addOrder">
update orders set num=num+1 where id=1;
</update>
</mapper>
@Service
public class OrderService {
@Resource
private OrderMapper orderMapper;
public void addOrder(){
orderMapper.addOrder();
}
}
@RestController
public class OrderController {
@Resource
OrderService orderService;
@Resource
MoneyFeign moneyFeign;
@RequestMapping("/order")
@GlobalTransactional
public String Order(){
moneyFeign.money();
int i = 1/0;
orderService.addOrder();
return "下单成功";
}
}
@SpringBootApplication
@EnableFeignClients
@MapperScan("com.example.consumer.mapper")
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
如果Controller不加@GlobalTransitional注解,最后支付表-10元,但是订单表不变;
加上分布式事务后,两表都不发生变化