使用Seata实现分布式事务
一、安装与配置
1、下载Seata
点击进入下载页面(搜索Assets关键字,找到下载位置)
2、修改配置文件
D:\workset\javaset\seata-server-1.3.0\seata\conf\
1、将mode = "file" 改为 mode = "db"
2、修改db的配置信息 driverClassName = "com.mysql.cj.jdbc.Driver" url = "jdbc:mysql://localhost:3306/sltest?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai" user = "sl" password = "123456"
D:\workset\javaset\seata-server-1.3.0\seata\conf\registry.conf
1、将registry的type = "file" 改为 type = "nacos" 2、修改registry的nacos的配置信息 application = "seata-server" serverAddr = "127.0.0.1:8848" group = "SEATA_GROUP" namespace = "" cluster = "default" username = "nacos" password = "nacos" 3、将config的type = "file" 改为 type = "nacos" 4、修改config的nacos的配置信息 serverAddr = "127.0.0.1:8848" namespace = "" group = "SEATA_GROUP" username = "nacos" password = "nacos"
3、创建数据库表
点击这里可以下载,脚本如下
-- -------------------------------- The script used when storeMode is 'db' -------------------------------- -- the table to store GlobalSession data CREATE TABLE IF NOT EXISTS `global_table` ( `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `status` TINYINT NOT NULL, `application_id` VARCHAR(32), `transaction_service_group` VARCHAR(32), `transaction_name` VARCHAR(128), `timeout` INT, `begin_time` BIGINT, `application_data` VARCHAR(2000), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`xid`), KEY `idx_gmt_modified_status` (`gmt_modified`, `status`), KEY `idx_transaction_id` (`transaction_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store BranchSession data CREATE TABLE IF NOT EXISTS `branch_table` ( `branch_id` BIGINT NOT NULL, `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `resource_group_id` VARCHAR(32), `resource_id` VARCHAR(256), `branch_type` VARCHAR(8), `status` TINYINT, `client_id` VARCHAR(64), `application_data` VARCHAR(2000), `gmt_create` DATETIME(6), `gmt_modified` DATETIME(6), PRIMARY KEY (`branch_id`), KEY `idx_xid` (`xid`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store lock data CREATE TABLE IF NOT EXISTS `lock_table` ( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(96), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_branch_id` (`branch_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
双击启动 D:\workset\javaset\seata-server-1.3.0\seata\bin\seata-server.bat,Seata已经注册到Nacos里面了
4、将配置注册到Nacos注册中心
点击这里下载seata-1.3.0项目 下载后将整个script文件夹copy到Seata的根目录下
源路径和copy后的路径分别为:
原路径(seata-1.3.0源码中):C:\Users\shenl\Downloads\seata-1.3.0\script
copy到的路径(seata-1.3.0的window安装路径):D:\workset\javaset\seata-server-1.3.0\seata\script
修改文件 D:\workset\javaset\seata-server-1.3.0\seata\script\config-center\config.txt
1、将store.mode=file 改为 store.mode=db 2、修改db的配置信息 store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.cj.jdbc.Driver store.db.url=jdbc:mysql://localhost:3306/sltest?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai store.db.user=sl store.db.password=123456
备注:文件中的配置项service.vgroupMapping.my_test_tx_group=default需要与项目application.yaml中的配置一致
在git客户端执行脚本:sh nacos-config.sh,config.txt中的所有参数就配置到了Nacos中了
nacos-config.sh在此目录中找(D:\workset\javaset\seata-server-1.3.0\seata\script\config-center\nacos)
5、在所有用到分布式事务的数据库中添加表
CREATE TABLE `undo_log`( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, PRIMARY KEY(`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
二、构建项目
1、构建消费者项目
application.properties
# 应用名称 spring.application.name=service-consumer # 应用服务 WEB 访问端口 server.port=7010 # Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html # Nacos认证信息 spring.cloud.nacos.discovery.username=nacos spring.cloud.nacos.discovery.password=nacos # Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 # 注册到 nacos 的指定 namespace,默认为 public spring.cloud.nacos.discovery.namespace=public #配置seata的注册中心 seata.enabled=true seata.application-id=${spring.application.name} #配置事务分组 seata.tx-service-group=my_test_tx_group #与script\config-center\config.txt中配置一致 seata.registry.type=nacos seata.registry.nacos.server-addr=127.0.0.1:8848 seata.registry.nacos.application=seata-server seata.registry.nacos.namespace= seata.registry.nacos.username=nacos seata.registry.nacos.password=nacos seata.registry.nacos.group=SEATA_GROUP #配置seata的配置中心 seata.config.type=nacos seata.config.nacos.server-addr=127.0.0.1:8848 seata.config.nacos.namespace= seata.config.nacos.username=nacos seata.config.nacos.password=nacos seata.config.nacos.group=SEATA_GROUP spring.datasource.url=jdbc:mysql://localhost:3306/sltest?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai spring.datasource.username=sl spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.type=com.alibaba.druid.pool.DruidDataSource mybatis.type-aliases-package=com.leiyuke.cloud.consumer.pojo logging.level.root=info
pom.xml
<!-- nacos 服务注册发现(客户端)依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- nacos-config 配置中心依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!-- seata 分布式事务依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency> <!-- openfeign 远程调用依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
Application.java
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
IProductService.java
@FeignClient(name = "service-product",path = "stock") public interface IProductService { @RequestMapping("/subStock") String subProduct(); }
ConsumerServiceImpl.java
... ... @GlobalTransactional //分布式事务注解 public String addOrder() { Order order = orderMapper.getOrder(); String value = order.getValue(); Long num = Long.valueOf(value) + 1; String nextValue = String.format("%0"+6+"d",num); orderMapper.update(nextValue);//生成订单号,在数据库A //int a = 1/0; productService.subProduct();//库存减1,远程调用service-product微服务,在数据库B return "订单号为:"+nextValue; } ... ...
2、构建生产者项目
application.properties 只修改微服务名,端口,数据库配置,其余与消费者项目保持一致
pom.xml 与消费者项目保持一致
Application.java 启动类与消费者项目保持一致
GoodsServiceImpl.java
... ... public void update() { //int a = 1/0; goodsMapper.update(); } ... ...
三、测试
启动Nacos与Seata服务
启动消费者与服务者两个项目
1、给addOrder方法添加注解@GlobalTransactional
分别去掉两处的注释(int a = 1/0)语句,使程序运行出错,两个数据中的值都没有改变,事务生效
2、去掉addOrder方法的注解@GlobalTransactional
分别去掉两处的注释(int a = 1/0)语句,使程序运行出错,发现订单库数据已修改,库存数据没有改变,事务不生效