本文基于Seata 1.7.0搭建分布式事务Demo,实现简单的转账业务。使用Nacos作为配置中心和注册中心,使用Feign远程调用微服务。
本Demo工程的Git地址为:https://gitee.com/SX-Code/dtx-seata-demo
业务流程
转账流程
Seata执行流程
其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。
细节说明:
TM 请求 TC 开启一个全局事务。TC 会生成一个 XID 作为该全局事务的编号。
XID,会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起。
RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联。
TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。
TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。
Seata Server搭建
服务端使用Nacos作为配置中心和注册中心,数据库则选择mysql。
下载 Seata
Seata TC服务源码下载地址:
https://github.com/seata/seata/releases
选择想要的 Seata 版本。这里,我们选择 v1.7.0 最新版本。
进入/opt
目录:
|
下载文件
|
解压:
|
查看目录:
|
配置 Seata
进入/opt/seata/conf/
目录编辑配置文件application.yml
,参考配置信息在application.example.yml
中,可以参考着配置。
|
剩余的配置我们放在Nacos配置中心
所有可配置内容都在
/opt/seata/script/config-center/config.txt
Data ID:seataServer.properties
Group:SEATA_GROUP
配置内容:
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8&allowPublicKeyRetrieval=true
store.db.user=root
store.db.password=root
store.db.minConn=10
store.db.maxConn=100
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.lockTable=lock_table
store.db.distributedLockTable=distributed_lock
store.db.queryLimit=1000
store.db.maxWait=5000
初始化数据库
创建数据库seata
,并使用 mysql.sql
脚本,初始化 Seata TC Server 的 db 数据库。脚本内容如下:
数据库脚本文件在
/opt/seata/script/server/db/mysql.sql
|
启动 TC Server
进入到/opt/seata/bin
,执行如下命令
|
使用下面命令查看日志:
|
可以在Nacos中查看服务,集群也是正常的
准备数据库
准备2个数据:bank1
和bank2
,两个数据库都有两个表account_info
和undo_log
,表结构一致。
数据库bank1
表的脚本文件:
|
数据库bank2
表的脚本文件
|
两个数据的undo_log
表如下:
|
准备微服务项目
项目使用微服务分模块开发方式,两个服务之间通过Feign进行远程调用。
项目结构
创建一个父工程dtx-seata-demo
作版本控制,在其下创建两个子模块提供服务
|
版本控制
在父工程dtx-seata-demo
的pom文件中添加版本控制信息
|
完善转账服务
打开子模块dtx-seata-bank1
,该模块操作数据库bank1
。在减去自身金额时,使用Feign远程调用服务增加bank2
的金额。
依赖信息
添加服务所需依赖信息:
|
配置信息
在dtx-seata-bank1
模块的resources
下创建bootstrap.yml
,其中配置项目端口和路径、数据库源、Nacos注册中心、开启Feign熔断降级、配置Seata,以便找到Seata TC。
|
启动类
在包com.swx.bank1
下创建启动类
|
实体类
在包com.swx.bank1.entry
下创建AccountInfo
实体类
|
DAO层
该层负责操作数据库,在包com.swx.bank1.mapper
下创建AccountInfoMapper
接口
|
这个增加金额的SQL语句漏洞百出,但这不影响测试Seata分布式事务,请忽略!
FeignClient
该层负责远程调用,在包com.swx.bank1.spring
下创建Bank2Client
接口
|
添加熔断降级,请求失败时的操作
|
Service层
该层负责操作事务,在包com.swx.bank1.service
下创建AccountInfoService
接口
|
在包com.swx.bank1.service.impl
下实现该接口:
@Transactional(rollbackFor = Exception.class)为开启本地事务
@GlobalTransactional为开启全局事务,Seata事务
|
Controller层
|
复制转账服务
将转账业务复制一份到dtx-seata-bank2
中,将所有 bank1 更改为 bank2
例如配置文件更该为如下:
|
Service更改如下:
|
测试事务
正常流程
浏览器访问:http://localhost:56081/bank1/transfer?amount=100
调用者异常
浏览器访问:http://localhost:56081/bank1/transfer?amount=2
被调者异常
浏览器访问:http://localhost:56081/bank1/transfer?amount=3
分布式服务失效
Feign导致XID为null
使用Feign远程调用,被调方法打印的XID为null。检查自己的Maven依赖,确保引入的是
|