1、seata下载
seata-server:
2、技术选型及版本信息
注册中心:eureka
服务调用:feign
持久层框架:mybatis 2.1.1
数据库:mysql 8.0.17
springboot:2.2.2.RELEASE
springcloud:Hoxton.SR1
jdk:1.8
seata:1.0.0
3、服务结构
事务发起方服务(TM):commodity-service
事务方服务(RM):orders-service、pay-service、stock-service、score-service、store-service
事务协调方服务(TC):/seata-1.0.0/server(seata-server包解压得到)
4、服务方配置修改(seata-server)
(1)、resources/registry.conf
#服务注册中心配置
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "***eureka***" #修改为注册中心使用eureka
nacos {
serverAddr = "localhost"
namespace = ""
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:50000/eureka" #修改为eureka地址
application = "seata-server" #修改为seata-server服务名称
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"
}
}
(2)、resources/file.conf
service {
#transaction service group mapping
vgroup_mapping.fsp_tx_group = "seata-server" #修改这里,fsp_tx_group这个事务组名称是自定义的,但一定要与client端的这个配置一致!否则会报错!seata-server为seata-server服务名称
#only support when registry.type=file, please don't set multiple addresses
seata-server.grouplist = "127.0.0.1:8091" #如果上面fsp_tx_group的值修改为了seata-server,此处也修改为seata-server(默认名称为default)
#disable seata
disableGlobalTransaction = false
}
## transaction log store, only used in seata-server
store {
## store mode: file、db
mode = "db" #如果使用数据库存储事务信息,将值修改为db
## file store property
file {
## store location dir
dir = "sessionStore"
}
## database store property mode=db时,事务日志会存储在这个配置的数据库里
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "druid" #修改为自己的数据源类型
## mysql/oracle/h2/oceanbase etc.
db-type = "mysql" #修改为自己的数据库类型
driver-class-name = "com.mysql.cj.jdbc.Driver" #修改数据库驱动
url = "jdbc:mysql://localhost:3306/seata_server?serverTimezone=Asia/Shanghai" #修改链接数据库的url
user = "root" #数据库登录用户
password = "xzc" #数据库登录密码
}
}
(3)、如果第(2)步中事务日志采用数据库存储,需要创建三张表global_table,branch_table,lock_table
-- -------------------------------- 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,
`gmt_modified` DATETIME,
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;
配置修改完成,等eureka启动后,启动seata-server
5、客户端配置
共有6个客户端,每个客户端都要做如下配置(只列举跟seata有关的配置):
(1)seata依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
(2)、application.properties
#设置seata事务组
spring.cloud.alibaba.seata.tx-service-group=fsp_tx_group
事务组名称一定要跟seata-server中配置的一样
(3)、resources目录下新增registry.conf和file.conf文件
registry.conf:
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "eureka" #修改为采用eureka为注册中心
nacos {
serverAddr = "localhost"
namespace = ""
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:50000/eureka" #修改为eureka注册中心地址
application = "commodity-service" #修改为本服务的服务名
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"
}
}
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->rgroup
vgroup_mapping.fsp_tx_group = "seata-server" #fsp_tx_group为事务组,与seata-server保持一致,seata-server为事务服务的名称(默认名称为default),也与seata-server中保持一致
#only support single node
seata-server.grouplist = "127.0.0.1:8091" #此处seata-server与上面fsp_tx_group的值保持一致
#degrade current not support
enableDegrade = false
#disable
disable = false
#unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
max.commit.retry.timeout = "-1"
max.rollback.retry.timeout = "-1"
}
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 {
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"
}
support {
## spring
spring {
# auto proxy the DataSource bean
datasource.autoproxy = false
}
}
(4)、配置代理数据源
由于seata对数据源做了代理和接管,因此在每个分布式服务中,都要配置代理数据源
package com.xzc.esc.commodityservice.configs;
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
/**
* seata代理数据源配置
* seata对数据源做了代理和接管,在每个参与分布式事务的服务中,都要做如下配置
*/
@Configuration
public class DatasourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
@Primary
@Bean("dataSource")
public DataSourceProxy dataSource(DataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:/mapper/*.xml"));//必须指定mapper的路径,否则会报Invalid bound statement (not found)错
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return sqlSessionFactoryBean.getObject();
}
}
(5)、解决循环依赖
服务启动类中,SpringBootApplication中需要排除自动配置类,以解决循环依赖
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
(6)、新增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 DEFAULT CHARSET=utf8;
注:以上6个步骤是每个微服务都需要做的步骤
(7)、添加@GlobalTransactional注解
在事务调用服务上添加@GlobalTransactional,如下:
按照以上步骤就可以配置出基于springcloud、eureka、feign、mybatis、seata的分布式事务服务
参考:
seata-samples
https://github.com/seata/seata-samples/tree/master/springcloud-eureka-feign-mybatis-seata
http://seata.io/zh-cn/index.html