假设系统有两个服务,订单服务,与库存服务。购买商品时,订单服务先创建订单,再扣除库存。这两个事务要保持一致性。
seata可以到官方的git上下载。https://github.com/seata/seata/releases。可以选择下载linux版本的,也可以下载window的安装包。我下载了1.1.0版本的window安装包。下载解压后里边就有三个文件夹bin,conf,lib。其实seata也是一个web项目,需要把它注册到eureka上。
进入conf文件夹,修改file.conf、register.conf。
修改file.conf,使用数据库的话,修改mode = “db”,然后下面的db就按照自己的数据库填。
如下图
修改register.conf,把type修改成eureka,然后填写自己eureka的地址。注意:application就是你这个seata服务注册到eureka上的名字,相当于springboot项目的application.name。
type = "eureka"
eureka {
serviceUrl = "http://127.0.0.1:8000/eureka"
application = "seata"
weight = "1"
}
然后数据库里还需要用到几个控制事务的表global_table、branch_table、lock_table
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table` (
`branch_id` BIGINT(20) NOT NULL,
`xid` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`transaction_id` BIGINT(20) NULL DEFAULT NULL,
`resource_group_id` VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`resource_id` VARCHAR(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`branch_type` VARCHAR(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`status` TINYINT(4) NULL DEFAULT NULL,
`client_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`application_data` VARCHAR(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`gmt_create` DATETIME(0) NULL DEFAULT NULL,
`gmt_modified` DATETIME(0) NULL DEFAULT NULL,
PRIMARY KEY (`branch_id`) USING BTREE,
INDEX `idx_xid`(`xid`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Table structure for global_table
-- ----------------------------
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table` (
`xid` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`transaction_id` BIGINT(20) NULL DEFAULT NULL,
`status` TINYINT(4) NOT NULL,
`application_id` VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`transaction_service_group` VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`transaction_name` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`timeout` INT(11) NULL DEFAULT NULL,
`begin_time` BIGINT(20) NULL DEFAULT NULL,
`application_data` VARCHAR(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`gmt_create` DATETIME(0) NULL DEFAULT NULL,
`gmt_modified` DATETIME(0) NULL DEFAULT NULL,
PRIMARY KEY (`xid`) USING BTREE,
INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE,
INDEX `idx_transaction_id`(`transaction_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Table structure for lock_table
-- ----------------------------
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table` (
`row_key` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`xid` VARCHAR(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`transaction_id` BIGINT(20) NULL DEFAULT NULL,
`branch_id` BIGINT(20) NOT NULL,
`resource_id` VARCHAR(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`table_name` VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`pk` VARCHAR(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`gmt_create` DATETIME(0) NULL DEFAULT NULL,
`gmt_modified` DATETIME(0) NULL DEFAULT NULL,
PRIMARY KEY (`row_key`) USING BTREE,
INDEX `idx_branch_id`(`branch_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
主要是一些配置问题,将一个订单配置,库存的直接复制就可以了。
pom.xml 引入依赖,别引错,否则会报错。
com.alibaba.cloud
spring-cloud-starter-alibaba-seata
2.1.1.RELEASE
application.yml中配置seata事务组 ,注意配置的事务组要与file.conf中的vgroup_mapping.xxx 的xxx一致。
spring:
# seata分组
cloud:
alibaba:
seata:
tx-service-group: seata_group
在项目的resource下,添加两个配置文件,file.conf与register.conf(register可以直接复制上边改好的register.conf)。在刚刚安装的seata里的conf文件夹中,有一个file.conf.example文件,这个是客户端的file.conf。把它改名成file.conf,复制到项目下,修改其中的service,和store。需要注意的就是vgroup_mapping.xxx= “yyy”,这里的xxx就是上边yml中配置的事务组,yyy就是最上边配置seata服务端时,注册到eureka上的服务名字。我刚刚填的是seata,这里也填seata。
service {
#transaction service group mapping
vgroup_mapping.seata_group = "seata"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "127.0.0.1:8091"
#degrade, current not support
enableDegrade = false
#disable seata
disableGlobalTransaction = false
}
store {
## store mode: file、db
mode = "db"
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "druid"
## mysql/oracle/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/alipay"
user = "root"
password = "root"
minConn = 1
maxConn = 10
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
}
}
最后可能还需要添加一个undo_log表。
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime(0) NOT NULL,
`log_modified` datetime(0) NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
由于是简单的例子,我没有分库,所以我把这几个用到表都放到一个库里了。global_table、branch_table、lock_table这三个放到seata服务端配置的数据库,undo_log放在订单服务配置的数据库。
在需要使用到事务的服务中加上一个配置类,并且在启动类上添加注解@Import(DataSourceProxyAutoConfiguration.class)。
@Configuration
public class DataSourceProxyAutoConfiguration {
/**
* 数据源属性配置
* {@link DataSourceProperties}
*/
private DataSourceProperties dataSourceProperties;
public DataSourceProxyAutoConfiguration(DataSourceProperties dataSourceProperties) {
this.dataSourceProperties = dataSourceProperties;
}
/**
* 配置数据源代理,用于事务回滚
*
* @return The default datasource
* @see DataSourceProxy
*/
@Primary
@Bean("dataSource")
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(dataSourceProperties.getUrl());
dataSource.setUsername(dataSourceProperties.getUsername());
dataSource.setPassword(dataSourceProperties.getPassword());
dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
return new DataSourceProxy(dataSource);
}
//在启动类上添加注解
@Import(DataSourceProxyAutoConfiguration.class)
public class SeataAlipayApplication
最后,在需要用到事务的接口上添加@GlobalTransactional注解就可以了。
@GlobalTransactional
public String createOrder(String did)
小白弄了一天- -,主要是安装配置seata时比较麻烦,使用的话直接使用一个@GlobalTransactional注解就可以了,非常方便