Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
Transaction ID XID
: 全局唯一事务ID
TC
(Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM
(Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM
(Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚.
下载地址
## transaction log store
store {
## store mode: file、db
mode = "file"
## 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 = "mysql"
password = "mysql"
min-conn = 1
max-conn = 3
global.table = "global_table"
branch.table = "branch_table"
lock-table = "lock_table"
query-limit = 100
}
}
修改为
## transaction log store
store {
## store mode: file、db
mode = "db" # 这里改为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://localhost:3306/seata"
user = "mysql"
password = "mysql"
min-conn = 1
max-conn = 3
global.table = "global_table"
branch.table = "branch_table"
lock-table = "lock_table"
query-limit = 100
}
}
注意:0.9.0版本的seata,匹配不了mysql8,可以匹配mysql5.7
创建3个微服务,一个订单服务,一个库存服务,一个账户服务。
当用户下单时,会在订单服务中创建一个订单,然后通过远程调用库存服务来扣减下单的库存,在通过远程调用账户服务来扣减用户账户里面的余额,最后在订单服务中修改订单状态为已完成。
下订单 --》 扣库存 --》 减账户(余额)
创建3个业务数据库
需要在三个业务库中创建对应的回滚日志表
注意:每个业务库都需要创建回滚日志表
建表sql在seata的解压目录 seata-0.9.0\seata\conf\db_undo_log.sql
-- the table to store seata xid data
-- 0.7.0+ add context
-- you must to init this sql for you business databese. the seata server not need it.
-- 此脚本必须初始化在你当前的业务数据库中,用于AT 模式XID记录。与server端无关(注:业务数据库)
-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
drop table `undo_log`;
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;
新建3个微服务
seata-order-service2001
seata-storage-service2002
seata-account-service2003
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-seataartifactId>
<exclusions>
<exclusion>
<groupId>io.seatagroupId>
<artifactId>seata-allartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>io.seatagroupId>
<artifactId>seata-allartifactId>
<version>0.9.0version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>com.zh.springcloudgroupId>
<artifactId>coreartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
# ===== nacos 配置 =====
server:
port: 2001
spring:
application:
name: seata-order-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos注册中心地址
alibaba:
seata:
# 自定义事务组名称需要与seata-server中file.conf 配置文件中配置的一致
tx-service-group: zh_tx_group
datasource:
url: jdbc:mysql://101.201.249.238:33306/seata_order?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowMutiQueries=true
#root
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
#mybatis plus 设置
mybatis-plus:
mapper-locations: classpath*:mapper/*Mapper.xml
global-config:
# 关闭MP3.0自带的banner
banner: false
db-config:
#主键类型 0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)";
id-type: 3
# 默认数据库表下划线命名
table-underline: true
# 逻辑已删除值(默认为 1)
logic-delete-value: 1
# 逻辑未删除值(默认为 0)
logic-not-delete-value: 0
configuration:
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 返回类型为Map,显示null对应的字段
call-setters-on-nulls: true
# 关闭feign对hystrix的支持
feign:
hystrix:
enabled: false
@SpringBootApplication
@EnableDiscoveryClient // 开启nacos客户端
@EnableFeignClients // 开启feign 客户端
@MapperScan("com.zh.springcloud.mapper") // mapper扫描路径
public class SeataOrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SeataOrderServiceApplication.class,args);
}
}
/**
* 数据源代理
*/
@Configuration
public class DataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
@Primary
@Bean("dataSource") //6.1 创建DataSourceProxy
public DataSourceProxy dataSourceProxy(DataSource druidDataSource){
return new DataSourceProxy(druidDataSource);
}
// 因为mybatis-plus 中自动代理了SqlSessionFactoryBean,所以这里不用写
// @Bean //6.2 将原有的DataSource对象替换为DataSourceProxy
// public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy)throws Exception{
// SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// sqlSessionFactoryBean.setDataSource(dataSourceProxy);
// sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
// .getResources("classpath*:mapper/*Mapper.xml"));
// sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
// return sqlSessionFactoryBean.getObject();
// }
}
注意:我这里是使用了mybatis-plus, 因为mybatis-plus自动重写了SqlSessionFactory,所以我这里不用写,如果你使用的是mybatis,就要自己从新代理。
先启动 nacos,在启动seata, 最后启动2001服务,发现报错
经排查问题发现,必须在 resources 目录下新增 file.conf 和 registry.conf配置文件
然后重新打包,再次启动发现正常启动
在业务类源头添加注解 @GlobalTransactional(name = "zh-create-order",rollbackFor = Exception.class)
经测试发现,当发生异常是,数据会回滚,来保证数据的一致性。