SpringCloud集成Seata2.0实现分布式事务
关于seata的原理和实现逻辑网上很多就不再赘述l。干脆直接一点,直接讲该如何实现分布式事务。
1、下载seata
https://seata.apache.org/zh-cn/unversioned/download/seata-server/
下载2.0版本
2、在数据库中新建seata数据库
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/016bf69d00464f8ebad01a8578edff73.png)
在seata数据库中执行文件路径下对应的数据库脚本,这里用的是mysql
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/db3ffb13407943ad84316d21662044f9.png)
关键点来了:
编辑seata的配置文件
server:
port: 7091
spring:
application:
name: seata-server
logging:
config: classpath:logback-spring.xml
file:
path: ${log.home:${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:
server-addr: 192.168.5.40:8848
namespace: b137693a-b922-4384-a8f0-cc9d0ab35fd9
group: SEATA_GROUP
username:
password:
context-path:
data-id: seataServer.properties
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
application: seata-server
server-addr: 192.168.5.40:8848
group: SEATA_GROUP
namespace: b137693a-b922-4384-a8f0-cc9d0ab35fd9
cluster: default
username:
password:
context-path:
data-source-proxy-mode: AT
server:
service-port: 8091 #If not configured, the default is '${server.port} + 1000'
max-commit-retry-timeout: -1
max-rollback-retry-timeout: -1
rollback-retry-timeout-unlock-enable: false
enable-check-auth: true
enable-parallel-request-handle: true
enable-parallel-handle-branch: false
retry-dead-threshold: 130000
xaer-nota-retry-timeout: 60000
enableParallelRequestHandle: true
recovery:
committing-retry-period: 1000
async-committing-retry-period: 1000
rollbacking-retry-period: 1000
timeout-retry-period: 1000
undo:
log-save-days: 7
log-delete-period: 86400000
session:
branch-async-queue-size: 5000 #branch async remove queue size
enable-branch-async-remove: false #enable to asynchronous remove branchSession
store:
# support: file 、 db 、 redis 、 raft
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
user: root
password: root
min-conn: 10
max-conn: 100
global-table: global_table
branch-table: branch_table
lock-table: lock_table
distributed-lock-table: distributed_lock
query-limit: 1000
max-wait: 5000
metrics:
enabled: false
registry-type: compact
exporter-list: prometheus
exporter-prometheus-port: 9898
transport:
rpc-tc-request-timeout: 15000
enable-tc-server-batch-send-response: false
shutdown:
wait: 3
thread-factory:
boss-thread-prefix: NettyBoss
worker-thread-prefix: NettyServerNIOWorker
boss-thread-size: 1
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/metadata/v1/**
如果和我一样用的nacos,改一下地址server-addr,group,和namespace就行。
db:中改为你的数据库配置
nacos中增加配置
# 存储模式
store.mode=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
# 注意根据生产实际情况调整参数host和port
store.db.url=jdbc:mysql://127.0.0.1:3306/seata
# 数据库用户名密码
store.db.user=root
store.db.password=root
# 微服务里配置与这里一致
service.vgroupMapping.fsp_tx_group=default
service.vgroupMapping.fsp_tx_group=default
service.vgroupMapping.fsp_tx_group=default
service.vgroupMapping.default=default
双击启动seata
在nacos中可以看到seata已经注册了。
<!--seata分布式事务-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
在服务中引用上面的依赖,这个依赖重写了fegin调用保证了多个服务之间seata的XID一致。
服务配置文件yml中增加seata配置
seata:
registry:
type: nacos
nacos:
server-addr: 192.168.5.40:8848
namespace: b137693a-b922-4384-a8f0-cc9d0ab35fd9
group: SEATA_GROUP
application: seata-server
# 事务组名称
tx-service-group: default
service:
# 事务组与tc集群的映射关系
vgroup-mapping:
# default - tc服务的集群名称
default: default
enabled: true
在业务数据库中(不是seata数据库,而是微服务的业务数据库)
建测试表进行测试。
创建undo_log表给seata用
CREATE TABLE `undo_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`branch_id` bigint NOT NULL,
`xid` varchar(100) COLLATE utf8mb4_general_ci NOT NULL,
`context` varchar(128) COLLATE utf8mb4_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=570 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
服务A代码,t2会报空指针
@GlobalTransactional(rollbackFor = Exception.class)
@GetMapping(value = "/test")
public ResultBean test() {
System.out.println("事务id---------------------->" + RootContext.getXID());
Test1 t1 = new Test1();
t1.setName("user插入");
testMapper.insert(t1);
System.out.println("user插入Success====================================");
externalFeignClient.exSeataTest();
Test1 t2 = null;
t2.getName();
return ResultBean.success();
}
服务B
@GlobalTransactional(rollbackFor = Exception.class)
@GetMapping(value = "/exSeataTest")
public ResultBean exSeataTest(){
System.out.println("事务id---------------------->" + RootContext.getXID());
Test1 t1 = new Test1();
t1.setName("ext插入");
testMapper.insert(t1);
System.out.println("ext插入Success====================================");
return ResultBean.success();
}
服务A在调用服务B,插入两条数据后,会报空指针,回滚插入的两条数据。
只要在调用远程的服务接口上加上注解就可以实现分布式事务了,被调用的接口上也加@GlobalTransactional(rollbackFor = Exception.class)注解
。
http://localhost:7091/端口是seata的可视化界面。密码在seata的配置文件中配置,我配置的是seata seata。
需要注意的几点:
1、被调用的服务的异常不能被全局异常器捕获,异常被捕获后,seata会认为没有发送异常,无法进行事务回滚,原理和@Transaction注解失效原理差不多。解决方法是通过校验调用其他服务的返回码是不是正常,不正常手动抛出一个异常,让seata取感知,从而回滚多个服务之间的事务。
ResultBean res = externalFeignClient.exSeataTest();
if (200 != res.getCode()) {
throw new BusinessException();
}
2、服务A和服务B的yml中seata配置要一样