分布式事务有以下解决方案:
- XA
- TCC
- Seata 框架
- AT 事务
- SAGA
- 可靠消息最终一致性
- 最大努力通知
Seata 是一款开源一套一站式分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
Seata ATSeata 的 AT 模式(Automatic Transaction)是一种无侵入的分布式事务解决方案
链接一:Seata AT具体工作机制
TC(Transaction Coordinator),事务协调器
因各服务之间无法相互感知事务是否执行成功,这时就需要一个专门的服务,来协调各个服务的运行状态。
TM(Transaction Manager,事务管理器),由 TM 向 TC 申请开启一个全局事务
RM(Resource Manager,资源管理器),RM 负责对分支事务(即微服务的本地事务)进行管理
链接二:Spring Cloud微服务添加 AT 分布式事务
Seata Server 就是 TC,直接从官方仓库下载启动即可,下载地址:https://github.com/seata/seata/releases
减小TC的压力,不存在集群,只能多创建几个事务组个管理各自的几个模块
Seata Server 配置电脑内存不够的话seata-server.bat中 – 使用内存设置成256M
%JAVACMD% %JAVA_OPTS% -server -Xmx2048m -Xms2048m -Xmn1024m -Xss512k -XX:Sur......
Seata Server 的配置文件有两个:
- seata/conf/registry.conf
- seata/conf/file.conf
registrv.conf --向注册中心注册
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
# 这里选择 eureka 注册配置
type = "eureka"
nacos {
......
}
# eureka的注册配置
eureka {
# 注册中心地址
serviceUrl = "http://localhost:8761/eureka"
# 向注册中心注册名字,注册的服务ID,默认default
application = "seata-server"
weight = "1"
}
redis {
......
}
......
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "eureka"
nacos {
serverAddr = "localhost"
namespace = ""
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
# application = "default"
# weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = "0"
password = ""
cluster = "default"
timeout = "0"
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
username = ""
password = ""
}
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、springCloudConfig
type = "file"
nacos {
serverAddr = "localhost"
namespace = ""
group = "SEATA_GROUP"
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
app.id = "seata-server"
apollo.meta = "http://192.168.1.204:8801"
namespace = "application"
}
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
username = ""
password = ""
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}
file.conf
file.conf – 事务组对应使用的事务协调器
registry.conf 文件中配置eureka 注册中心,eureka服务的连接地址、注册的服务名
指定file.conf-- seata server运行过程中在库中记录日志
config {
# file、nacos 、apollo、zk、consul、etcd3
# 在这里选择使用本地文件来保存配置
type = "file"
......
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
# 使用本地文件 在这里设置配置文件的文件名
name = "file.conf"
}
}
store {
## store mode: file、db、redis
# 这里选择数据库存储
mode = "db"
## file store property
file {
......
}
# 数据库存储
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
# 数据库连接配置
url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"
user = "root"
password = "root"
minConn = 5
maxConn = 30
# 事务日志表表名设置
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
## redis store property
redis {
......
}
}
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
#enable heartbeat
heartbeat = true
# the client batch send request enable
enableClientBatchSendRequest = true
#thread factory for netty
threadFactory {
bossThreadPrefix = "NettyBoss"
workerThreadPrefix = "NettyServerNIOWorker"
serverExecutorThread-prefix = "NettyServerBizHandler"
shareBossWorker = false
clientSelectorThreadPrefix = "NettyClientSelector"
clientSelectorThreadSize = 1
clientWorkerThreadPrefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
bossThreadSize = 1
#auto default pin or 8
workerThreadSize = "default"
}
shutdown {
# when destroy server, wait seconds
wait = 3
}
serialization = "seata"
compressor = "none"
}
service {
#transaction service group mapping
# order_tx_group 与 yml 中的 “tx-service-group: order_tx_group” 配置一致
# “seata-server” 与 TC 服务器的注册名一致
# 从eureka获取seata-server的地址,再向seata-server注册自己,设置group
vgroupMapping.order_tx_group = "seata-server"
#only support when registry.type=file, please don't set multiple addresses
order_tx_group.grouplist = "127.0.0.1:8091"
#degrade, current not support
enableDegrade = false
#disable seata
disableGlobalTransaction = false
}
client {
rm {
asyncCommitBufferLimit = 10000
lock {
retryInterval = 10
retryTimes = 30
retryPolicyBranchRollbackonConflict = true
}
reportRetryCount = 5
tablemetaCheckEnable = false
reportSuccessEnable = false
}
tm {
commitRetryCount = 5
rollbackRetryCount = 5
}
undo {
dataValidation = true
logSerialization = "jackson"
logTable = "undo_log"
}
log {
exceptionRate = 100
}
}
启动:双击seata/bin/seata-server.bat文件
或者在当前目录下CMD:seata-server.bat
报错:path、home环境
jdk新版不支持bat
使用哪个协调器
service{
VgroupMapping.事物组名="注册中心协调器名"
}
或者
service{
VgroupMapping.group="事物组名"
VgroupMapping.tv="注册中心协调器名"
}
application.yml
application.yml – 事务组命名
多个模块写一个事务组就行
spring:
......
cloud:
alibaba:
seata:
tx-service-group: order_tx_group
datasource:
url: jdbc:mysql:/l/seata_order?useUnicode=true&driver-class-name: com.mysql.cj.jdbc.Driveruser
name: root
password: root
jdbcUrl: ${spring. datasource.url}
......
在业务方法上,添加注解
@GlobalTransactional --用来启动全局事务,只在第一个模块(方法)添加。
@Transactional --控制本地事务
依赖:
1.3.0 2.0.0.RELEASE com.alibaba.cloud spring-cloud-alibaba-seata ${spring-cloud-alibaba-seata.version} seata-all io.seata io.seata seata-all ${seata.version}
错误:要么没配置,要么没配置正确
自己写数据源配置
通过创建数据源代理对象dataSourceProxy
配置类
import com.alibaba.druid.pool.DruidDataSource;
import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
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 javax.sql.DataSource;
@Configuration
public class DSAutoConfiguration {
// 创建原始数据源对象
// @ConfigurationProperties把配置的参数(yml中的spring.datasource)注入HikariDataSource对象
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource dataSource() {
return new HikariDataSource();
// return new DruidDataSource();
}
// 创建数据源代理对象,因为这里创建了两个同类型的对象,spring不知道选择谁,添加首先对象注解
@Primary // 首选对象
@Bean
public DataSource dataSourceProxy(DataSource ds) { //DataSource被代理的目标对象
return new DataSourceProxy(ds);
}
启动类中:
因为直接写了,就排除spring自己的默认配置类
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
yml
第二阶段提交
失败的话
删除日志,回滚



