SpringCloud-2.0-周阳(24. 分布式事务 - Seata)
Posted ABin-阿斌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud-2.0-周阳(24. 分布式事务 - Seata)相关的知识,希望对你有一定的参考价值。
- 声明:原文作者:csdn:yuan_404
文章目录
1 . 分布式事务的问题
- 在分布式之前
是一台电脑上包含所有的东西 —— 所有的数据、程序所有的内容 ……
- 慢慢向分布式演变
从 1 对 1 (一个程序对应一个数据库)
到 1 对 N (分库,一个程序对应多个数据库)
再 N 对 N (分布式微服务,多个微服务对应多个数据库)
- 分布式之后
举例:
- 单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三个服务来完成。
- 此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题没法保证。
- 一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题
2 . Seata 简介
-
官网地址 :http://seata.io/zh-cn/
-
Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务
-
一个 ID + 三个组件
- Transaction ID XID :全局唯一的事务ID
- Transaction Coordinator(TC) :事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
- Transaction Manager™ :控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
- Resource Manager(RM) :控制分支事务,负责分支注册,状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚;
- 分布式事务处理过程
- TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID
- XID在微服务调用链路的上下文中传播
- RM向TC注册分支事务,将其纳入XID对应全局事务的管辖
- TM向TC发起针对XID的全局提交或回滚决议;
- TC调度XID下管辖的全部分支事务完成提交或回滚请求。
3 . Seata-Server 安装
-
将 seata-server 解压到指定目录
-
修改 conf 目录下的 file.conf 文件
先对配置文件进行备份
-
在 mysql 中新建一个数据库 :seata
-
初始化数据库
运行 conf 目录下的 db_store.sql 文件
如果没有,请看,SQL 脚本文件地址(选择自己的数据库) :https://github.com/seata/seata/tree/develop/script/server/db
复制下来去 MySQL 执行 -
修改 conf 目录下的 registry.conf 文件
-
启动 Nacos
-
启动 seata-server
双击 seata\\bin\\seata-server.bat
4 . 数据库环境搭建
- 下面就按照该框架图进行设计
这里我们会创建三个服务 —— 一个订单服务,一个库存服务,一个账户服务。
- 当用户下单时,会在订单服务中创建一个订单,然后通过远程调用库存服务来扣减下单商品的库存,
- 再通过远程调用账户服务来扣减用户账户里面的余额,
- 最后在订单服务中修改订单状态为已完成。
该操作跨越三个数据库,有两次远程调用,很明显会有分布式事务问题。
-
启动 Nacos 、Seata
-
创建业务数据库
seata_order: 存储订单的数据库
seata_storage:存储库存的数据库
seata_account: 存储账户信息的数据库建表SQL :
CREATE DATABASE seata_order; CREATE DATABASE seata_storage; CREATE DATABASE seata_account;
-
seata_order 库下建 t_order 表
CREATE TABLE t_order( `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id', `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id', `count` INT(11) DEFAULT NULL COMMENT '数量', `money` DECIMAL(11,0) DEFAULT NULL COMMENT '金额', `status` INT(1) DEFAULT NULL COMMENT '订单状态:0:创建中; 1:已完结' ) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; SELECT * FROM t_order;
-
seata_storage 库下建 t_storage 表
CREATE TABLE t_storage( `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id', `total` INT(11) DEFAULT NULL COMMENT '总库存', `used` INT(11) DEFAULT NULL COMMENT '已用库存', `residue` INT(11) DEFAULT NULL COMMENT '剩余库存' ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO seata_storage.t_storage(`id`,`product_id`,`total`,`used`,`residue`) VALUES('1','1','100','0','100'); SELECT * FROM t_storage;
-
seata_account 库下建 t_account 表
CREATE TABLE t_account( `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id', `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id', `total` DECIMAL(10,0) DEFAULT NULL COMMENT '总额度', `used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用余额', `residue` DECIMAL(10,0) DEFAULT '0' COMMENT '剩余可用额度' ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO seata_account.t_account(`id`,`user_id`,`total`,`used`,`residue`) VALUES('1','1','1000','0','1000') SELECT * FROM t_account;
-
建立回滚日志表
找到 Seat 中回滚日志建表 SQL 脚本 :\\seata\\conf\\db_undo_log.sql
三个数据库都需要执行该脚本
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, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
5 . 代码环境搭建
5.1 订单模块
- 项目目录:
-
新建模块 :seata-order-service-2001
-
修改 POM
<dependencies> <!--nacos--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <artifactId>seata-all</artifactId> <groupId>io.seata</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>0.9.0</version> </dependency> <!--feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--web-actuator--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--mysql-druid--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.37</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
-
编写 YML
server: port: 2001 spring: application: name: seata-order-service cloud: alibaba: seata: #自定义事务组名称需要与seata-server中的对应 tx-service-group: fsp_tx_group nacos: discovery: server-addr: localhost:8848 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/seata_order?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 username: root password: 123456 feign: hystrix: enabled: false logging: level: io: seata: info mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.demo.springcloud.pojo
-
编写 file.conf 文件
transport # tcp udt unix-domain-socket type = "TCP" #NIO NATIVE server = "NIO" #enable heartbeat heartbeat = true #thread factory for netty thread-factory boss-thread-prefix = "NettyBoss" worker-thread-prefix = "NettyServerNIOWorker" server-executor-thread-prefix = "NettyServerBizHandler" share-boss-worker = false client-selector-thread-prefix = "NettyClientSelector" client-selector-thread-size = 1 client-worker-thread-prefix = "NettyClientWorkerThread" # netty boss thread size,will not be used for UDT boss-thread-size = 1 #auto default pin or 8 worker-thread-size = 8 shutdown # when destroy server, wait seconds wait = 3 serialization = "seata" compressor = "none" service vgroup_mapping.fsp_tx_group = "default" default.grouplist = "127.0.0.1:8091" enableDegrade = false disable = false max.commit.retry.timeout = "-1" max.rollback.retry.timeout = "-1" disableGlobalTransaction = false client async.commit.buffer.limit = 10000 lock retry.internal = 10 retry.times = 30 report.retry.count = 5 tm.commit.retry.count = 1 tm.rollback.retry.count = 1 ## transaction log store store ## store mode: file、db mode = "db" ## file store file dir = "sessionStore" # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions max-branch-session-size = 16384 # globe session size , if exceeded throws exceptions max-global-session-size = 512 # file buffer size , if exceeded allocate new buffer file-write-buffer-cache-size = 16384 # when recover batch read size session.reload.read_size = 100 # async, sync flush-disk-mode = async ## database store db ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc. datasource = "dbcp" ## mysql/oracle/h2/oceanbase etc. db-type = "mysql" driver-class-name = "com.mysql.jdbc.Driver" url = "jdbc:mysql://127.0.0.1:3306/seata" user = "root" password = "123456" min-conn = 1 max-conn = 3 global.table = "global_table" branch.table = "branch_table" lock-table = "lock_table" query-limit = 100 lock ## the lock store mode: local、remote mode = "remote" local ## store locks in user's database remote ## store locks in the seata's server recovery #schedule committing retry period in milliseconds committing-retry-period = 1000 #schedule asyn committing retry period in milliseconds asyn-committing-retry-period = 1000 #schedule rollbacking retry period in milliseconds rollbacking-retry-period = 1000 #schedule timeout retry period in milliseconds timeout-retry-period = 1000 transaction undo.data.validation = true undo.log.serialization = "jackson" undo.log.save.days = 7 #schedule delete expired undo_log in milliseconds undo.log.delete.period = 86400000 undo.log.table = "undo_log" ## metrics settings metrics enabled = false registry-type = "compact" # multi exporters use comma divided exporter-list = "prometheus" exporter-prometheus-port = 9898 support ## spring spring # auto proxy the DataSource bean datasource.autoproxy = false
-
编写 registry.conf
registry # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "nacos" nacos serverAddr = "localhost:8848" namespace = "" cluster = "default" eureka serviceUrl = "http://localhost:8761/eureka" application = "default" weight = "1" redis serverAddr = "localhost:6379" db = "0" zk cluster = "default" serverAddr = "127.0.0.1:2181" session.timeout = 6000 connect.timeout = 2000 consul cluster = "default" serverAddr = "127.0.0.1:8500" etcd3 cluster = "default" serverAddr = "http://localhost:2379" sofa serverAddr = "127.0.0.1:9603" application = "default" region = "DEFAULT_ZONE" datacenter = "DefaultDataCenter" cluster = "default" group = "SEATA_GROUP" addressWaitTime = "3000" file name = "file.conf" config # file、nacos 、apollo、zk、consul、etcd3 type = "file" nacos serverAddr = "localhost" namespace = "" consul serverAddr = "127.0.0.1:8500" apollo app.id = "seata-server" apollo.meta = "http://192.168.1.204:8801" zk serverAddr = "127.0.0.1:2181" session.timeout = 6000 connect.timeout = 2000 etcd3 serverAddr = "http://localhost:2379" file name = "file.conf"
-
实体类
-
CommonResult
@Data @AllArgsConstructor @NoArgsConstructor public class CommonResult<T> private Integer code; private String message; private T data; public CommonResult(Integer code, String message) this(code,message,null);
以上是关于SpringCloud-2.0-周阳(24. 分布式事务 - Seata)的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud-2.0-周阳(23. 熔断降级 - Sentinel)
SpringCloud-2.0-周阳(13. 分布式配置中心 - SpringCloud Config)
SpringCloud-2.0-周阳(14. 消息总线 - SpringCloud Bus)
SpringCloud-2.0-周阳(16. 请求链路追踪 - SpringCloud Sleuth)