SpringBoot+Seata简单使用
安装seata
seata官网安装教程
我选择了用docker下载选择1.6.1最新的版本,docker-compose启动,有个注意的细节,官网说明了
docker pull seataio/seata-server:1.6.1
先启动一次把application.yml配置文件放在宿主机中,(注意1.4.2之前是file.conf,配置写法也不相同)
docker run -d -p 8091:8091 -p 7091:7091 --name seata-serve seataio/seata-server:1.6.1
docker cp seata-serve:/seata-server/resources /home/seata/config
拷出后可以,可以选择修改application.yml再cp进容器,或者rm临时容器,如下重新创建,并做好映射路径设置,我选择了删除旧镜像使用docker-compose启动新的镜像。
找到/home/seata/config/resources/application.yml,修改对应的配置,详细配置文件里面有个 application.example.yml可以参考或者上官网查看
application.yml
server:
port: 7091 #界面管理端口
spring:
application:
name: seata-server #应用名称
logging:
config: classpath:logback-spring.xml
file:
path: ${user.home}/logs/seata
extend:
logstash-appender:
destination: 127.0.0.1:4560
kafka-appender:
bootstrap-servers: 127.0.0.1:9092
topic: logback_to_logstash
console:
user:
username: seata #账号
password: seata #密码
seata:
config:
# support: nacos, consul, apollo, zk, etcd3 #支持的数据存储类型
type: nacos #选择的数据类型为nacos
nacos:
server-addr: nacosIP:8848 #nacos地址
namespace: seata #nacos命名空间
group: SEATA_GROUP #组名
usernam: nacos #账号
password: xxx #密码
data-id: seataServer.properties #读取的配置文件,官网有
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
application: seata-server
server-addr: nacosIP:8848
group: SEATA_GROUP
namespace: seata
# tc集群名称
cluster: default
username: nacos
password: xxx #密码
# store:
# support: file 、 db 、 redis
# mode: file
# server:
# service-port: 8091 #If not configured, the default is '${server.port} + 1000'
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
dcoker-compose
version: "3"
services:
seata-server:
image: seataio/seata-server:1.6.1 #镜像
container_name: my-seata #容器名称
ports:
- "8091:8091"
- "7091:7091"
environment:
#一定要指定store_mode,因为application.yml没指定,如application.yml指定了这可以不指定。如无指定会出现全局数据开启失败的问题
- STORE_MODE=db #指定为数据库,配置文件写在nacos
# 以SEATA_IP作为host注册seata server
- SEATA_IP=xx.xx.xx.xx #该IP一定是要服务能访问到的地址
- SEATA_PORT=8091 #服务端口
volumes:
- "/home/seata/config/resources:/seata-server/resources"
- "/usr/share/zoneinfo/Asia/Shanghai:/etc/localtime" #设置系统时区
- "/usr/share/zoneinfo/Asia/Shanghai:/etc/timezone" #设置时区
nacos上的配置文件seataServer.properties,配置文件官方有官方配置
store.mode=db
#-----db-----
store.db.datasource=druid
store.db.dbType=mysql #指定数据库类型
# 需要根据mysql的版本调整driverClassName
# mysql8及以上版本对应的driver:com.mysql.cj.jdbc.Driver
# mysql8以下版本的driver:com.mysql.jdbc.Driver
store.db.driverClassName=com.mysql.cj.jdbc.Driver #在数据库执行select version()查看版本;
store.db.url=jdbc:mysql://xx.xx.xx.xx:3306/seata-server?useUnicode=true&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false
store.db.user= root
store.db.password=root
# 数据库初始连接数
store.db.minConn=1
# 数据库最大连接数
store.db.maxConn=20
# 获取连接时最大等待时间 默认5000,单位毫秒
store.db.maxWait=5000
# 全局事务表名 默认global_table
store.db.globalTable=global_table
# 分支事务表名 默认branch_table
store.db.branchTable=branch_table
# 全局锁表名 默认lock_table
store.db.lockTable=lock_table
# 查询全局事务一次的最大条数 默认100
store.db.queryLimit=100
# undo保留天数 默认7天,log_status=1(附录3)和未正常清理的undo
server.undo.logSaveDays=7
# undo清理线程间隔时间 默认86400000,单位毫秒
server.undo.logDeletePeriod=86400000
# 二阶段提交重试超时时长 单位ms,s,m,h,d,对应毫秒,秒,分,小时,天,默认毫秒。默认值-1表示无限重试
# 公式: timeout>=now-globalTransactionBeginTime,true表示超时则不再重试
# 注: 达到超时时间后将不会做任何重试,有数据不一致风险,除非业务自行可校准数据,否者慎用
server.maxCommitRetryTimeout=-1
# 二阶段回滚重试超时时长
server.maxRollbackRetryTimeout=-1
# 二阶段提交未完成状态全局事务重试提交线程间隔时间 默认1000,单位毫秒
server.recovery.committingRetryPeriod=1000
# 二阶段异步提交状态重试提交线程间隔时间 默认1000,单位毫秒
server.recovery.asynCommittingRetryPeriod=1000
# 二阶段回滚状态重试回滚线程间隔时间 默认1000,单位毫秒
server.recovery.rollbackingRetryPeriod=1000
# 超时状态检测重试线程间隔时间 默认1000,单位毫秒,检测出超时将全局事务置入回滚会话管理器
server.recovery.timeoutRetryPeriod=1000
在seata使用的数据库上建立这几张表,官方有sql
CREATE TABLE `branch_table` (
`branch_id` bigint NOT NULL,
`xid` varchar(128) NOT NULL,
`transaction_id` bigint DEFAULT NULL,
`resource_group_id` varchar(32) DEFAULT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`branch_type` varchar(8) DEFAULT NULL,
`status` tinyint DEFAULT NULL,
`client_id` varchar(64) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime(6) DEFAULT NULL,
`gmt_modified` datetime(6) DEFAULT NULL,
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `distributed_lock` (
`lock_key` char(20) NOT NULL,
`lock_value` varchar(20) NOT NULL,
`expire` bigint DEFAULT NULL,
PRIMARY KEY (`lock_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `global_table` (
`xid` varchar(128) NOT NULL,
`transaction_id` bigint DEFAULT NULL,
`status` tinyint NOT NULL,
`application_id` varchar(32) DEFAULT NULL,
`transaction_service_group` varchar(32) DEFAULT NULL,
`transaction_name` varchar(128) DEFAULT NULL,
`timeout` int DEFAULT NULL,
`begin_time` bigint DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`xid`),
KEY `idx_status_gmt_modified` (`status`,`gmt_modified`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `lock_table` (
`row_key` varchar(128) NOT NULL,
`xid` varchar(128) DEFAULT NULL,
`transaction_id` bigint DEFAULT NULL,
`branch_id` bigint NOT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`table_name` varchar(32) DEFAULT NULL,
`pk` varchar(36) DEFAULT NULL,
`status` tinyint NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`row_key`),
KEY `idx_status` (`status`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
AT模式还要在对应的业务库加上undo_log表
CREATE TABLE `undo_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`branch_id` bigint NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb3;
docker-compose up -d 后台启动seata
docker logs -f seata-server 查看执行日志
结合springboot实现AT模式
1.多数据源
<dependency>
<groupId>com.baomidougroupId>
<artifactId>dynamic-datasource-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>io.seatagroupId>
<artifactId>seata-spring-boot-starterartifactId>
<version>1.5.2version>
dependency>
seata:
enabled: true #开启
enable-auto-data-source-proxy: false #关闭数据源代理,这里不关闭会出现事务回滚问题,具体可以查看
application-id: ${spring.application.name} # Seata 应用编号,默认为 ${spring.application.name}
tx-service-group: mytest-group # Seata 事务组编号,用于 TC 集群名
registry:
type: nacos
nacos:
application: seata-server
server-addr: xx.xx.xx.xx:8848
namespace: 332a6313-c96c-4c5d-b3b5-3063093bf8f9
group: SEATA_GROUP
config:
type: nacos
nacos:
server-addr: xx.xx.xx.xx:8848
namespace: 332a6313-c96c-4c5d-b3b5-3063093bf8f9
group: SEATA_GROUP
# 服务配置项,对应 ServiceProperties 类
service:
# 虚拟组和分组的映射
vgroup-mapping:
multi-datasource-service-group: default
# 分组和 Seata 服务的映射
grouplist:
default: xx.xx.xx.xx:8091 #seata服务地址
调用前,远程服务器的remotest表无数据
执行全局事务,TM申请了XID
执行远程数据插入,数据表出现数据的remotest表会删除刚插入的数据
本地数据库主键冲突报错,会执行全局事务的回滚,发现远程数据库
2.配合feign
开启2个服务注册到nacos上
调用方法,TM申请到了XID
远程服务获取到了相同的xid
执行完数据操作后,该服务RM会告诉TC执行完成
本地方法报错RM会告诉TC执行回滚,远程数据的RM也会被告知进行回滚
feign把xid添加到head中
String xid = RootContext.getXID();
requestTemplate.header(RootContext.KEY_XID, xid);
public class FeignInterceptorConfig implements RequestInterceptor {
Logger log = LoggerFactory.getLogger(FeignInterceptorConfig.class);
public void apply(RequestTemplate requestTemplate) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (ObjectUtils.isEmpty(requestAttributes)){
return ;
}
ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
//可以在这里将自定义请求头传递进去, key 请求, value 值
//处理上游请求头信息,传递时继续携带
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
// 跳过 content-length
if ("content-length".equals(name)) {
continue;
}
requestTemplate.header(name, values);
}
//传递 seata 的 xid
String xid = RootContext.getXID();
requestTemplate.header(RootContext.KEY_XID, xid);
}
else {
log.info("feign interceptor error header:{}", requestTemplate);
}
}
}
如果开启了hystrix会导致添加的head失效,因为获取的ServletRequestAttributes是null,要修改hystrix的策略。
参考下文
hystrix开启导致feign添加head失效
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
timeout:
#如果enabled设置为false,则请求超时交给ribbon控制
enabled: true
isolation:
# 隔离策略
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 100000
优点:
1.业务侵入性低
2.TC(事务协调者)服务端能单独部署,利用nacos等注册中心能起多个
缺点: