一.下载seata本案例演示的seata 版本为 v1.4.2
相较于之前的版本,v1.4.2 版本支持从一个 Nacos dataId中获取到配置信息,不需要像之前版本那样子需要上传几十个配置项了
下载地址:https://github.com/seata/seata/releases
或者是这个下载地址:https://seata.io/zh-cn/blog/download.html
一共需要下载两个包,一个是源码的压缩包,一个是已经编译好的压缩包
编译包解压后的目录:
修改 seata-server 的注册中心配置
type默认file (这种方式需要把源码的file.conf文件复制到项目中,比较麻烦不推荐),改成nacos,然后再修改里面配置
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
application = "seata-server" #对应nacos注册后的服务名
serverAddr = "127.0.0.1:8848" #nacos服务的地址端口
group = "SEATA_GROUP" #在nacos中的分组
namespace = "" #指定在nacos的命名空间,可为空,默认使用 public
cluster = "default" #集群 默认的
username = "nacos" #nacos账号名称
password = "nacos" #nacos账号密码
}
}
修改 seata-server 的配置中心地址
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
serverAddr = "172.31.0.5:8848" #nacos服务的地址端口
namespace = "" #指定在nacos的命名空间,可以为空
group = "SEATA_GROUP" #在nacos中的分组
username = "nacos"
password = "nacos"
dataId = "seataServer.properties" # nacos中seata的配置文件的dataId(1.4.2版本以上才支持)
}
}
在 nacos 中添加 seata-server 的配置文件
配置文件在 seata-server 源码包下:/seata-1.4.2/script/config-center/config.txt。(这也是为啥要下载源码包的原因之一)
在之前配置的nacos的相对应的命名空间下,增加配置 seataServer.properties。(位置和名称要和在 registry.conf 中的 nacos 配置一致)
然后把 config.txt 里的内容拷贝进去,在保存
这里列举几个配置
# 数据存储方式,db代表数据库
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://IP:3306/test?useUnicode=true&rewriteBatchedStatements=true
store.db.user=test
store.db.password=test
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
# 事务、日志等配置
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
# 客户端与服务端传输方式
transport.serialization=seata
transport.compressor=none
# 关闭metrics功能,提高性能
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
数据库地址,用户名,密码都需要修改为自己的数据库信息
注意: 其中有一个事务分组的配置 my_test_tx_group,这个是可以自定义修改的,需要与项目中的事务分组配置保持一致。要与spring boot项目中的 application.yml配置文件的 spring.cloud.alibaba.seata.tx-service-group这个属性相同,否则会提示 no available server to connect
service.vgroupMapping.my_test_tx_group=default
修改 seate-server 的存储为MySQL(也可以redis,默认是file)
在上一步的 seataServer.properties 配置文件中,修改存储的配置
store.mode=db
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver #注意mysql8.0以上的驱动 ,MySQL5 的驱动用 com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true
store.db.user=root
store.db.password=root
在数据库中创建seata-server需要的表
在源码目录:seata-1.4.2/script/server/db/mysql.sql
拿到seata所依赖的表结构,创建seata库,然后执行
-- -------------------------------- 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(6),
`gmt_modified` DATETIME(6),
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(128),
`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;
分别是:分支表、全局表、锁表
启动 seata-server
windows 下点击 seata-server.bat
其他环境:
sh ./bin/seata-server.sh -p 8091 -h 127.0.0.1 -m db
参数 | 全写 | 作用 | 备注 |
---|---|---|---|
-h | -host | 指定在注册中心注册的IP | 在不指定时获取当前的IP,外部访问部署在云环境和容器中的 server 建议指定 |
-p | -port | 指定server启动的端口号 | 默认为 8091 |
-m | -storeMode | 事务日志存储方式 | 支持file,db,redis。默认为file 注:redis需 seata-server 1.3 版本及以上 |
-n | -serverNode | 用于指定 seata-server节点ID | 如1,2,3… 。默认为1 |
-e | -seataEnv | 指定 seata-server 运行环境 | 如 dev, test等,服务启动时会使用 registry-dev.conf 这样的配置 |
解决方法:手动在nacos上添加配置
service.vgroupMapping.my_test_tx_group
参考其他人的教程时有讲到这一个坑,我个人在启动时没有遇到该问题,这里做个记录。
看到有些教程需要修改file.conf配置文件,我本身没有做修改,这里单纯做个记录。
修改配置文件:file.conf
#修改数据库地址,注意mysql5/mysql8驱动不同
store {
## store mode: file、db、redis
mode = "db"
## database store property
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"
## mysql 5.xx版本的driverClassName :
## driverClassName = "com.mysql.jdbc.Driver"
## mysql 8.0的driverClassName :
driverClassName = "com.mysql.cj.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true"
## 自己数据库的登录名和密码
user = "root"
password = "root"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}
主要是修改两个地方:
- mode = “file” 修改为 mode=“db”
- driverClassName 换成自己 MySQL5 或者 8 版本的驱动,并把数据库修改为自己的数据库信息
@RestControllerAdvice 这个注解把服务抛出的异常给提前捕捉处理掉了,导致 seata 无法捕获到该异常,导致 seata 认为本次没有问题,而执行commit *** 作,不进行回滚。
既然发现问题了,解决办法也简单,就是写个aop 切面,进行拦截异常,并手动进行回滚。
@Aspect
@Component
@Slf4j
public class SeataTransactionAspect {
@Before("execution(* com.pig.test.*.service.*.*(..))")
public void before(JoinPoint joinPoint) {
String xid = RootContext.getXID();
if (StringUtils.hasText(xid)) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
log.info("分布式事务开启: xid={}, method={}", xid, method.getDeclaringClass() + "." + method.getName());
}
}
@AfterThrowing(throwing = "e", pointcut = "execution(* com.pig.test.*.service.*.*(..))")
public void doRecoveryActions(Throwable e) throws TransactionException {
String xid = RootContext.getXID();
if (StringUtils.hasText(xid)) {
log.info("分布式事务方法执行异常,进行事务回滚,xid={}, e={}", xid, e.getMessage());
GlobalTransactionContext.reload(xid).rollback();
}
}
}
2. 使用 feign 降级,导致回滚失败
当项目中使用了 feign 服务降级
feign:
hystrix:
enabled: true
@Component
@Slf4j
public class CreditFallBackFactory implements FallbackFactory<CreditFeign> {
@Autowired
private WorkAspect workAspect;
@Override
public CreditFeign create(Throwable throwable) {
/**
* 手动进行事务回滚
*/
try {
this.workAspect.doRecoveryActions(throwable);
} catch (TransactionException e) {
e.printStackTrace();
}
return new CreditFeign() {
@Override
public void increaseAmount(Bonus bonus) {
log.info("服务降级 {}", throwable);
}
};
}
}
参考:
https://blog.csdn.net/zhujuntiankong/article/details/121306799
https://blog.csdn.net/weixin_38568503/article/details/120959245
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)