Spring Cloud 整合最新版seata分布式事务(六)
服务之间异常怎么办?事务怎么解决呢?
前言
现在我有 订单系统, 支付系统.注册中心 每个系统都是单独的,事务也都是本地事务,也是独立的.那么问题来了. 我--->订单系统-->支付系统.-->订单系统--->我 支付系统成功了 ,自己的事务也提交了. 到订单系统结果失败了.(结果就是钱付了,订单还未支付.)这样就存在了问题呀. 更多的问题也存在库存系统,加库存,改订单状态,支付等等,每个单独的模块事务怎么保持统一呢!!
seata 介绍
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案.
seata各种官方例子连接:https://github.com/seata/seata-samples seata服务端下载链接:https://github.com/seata/seata/releases
根据例子,我搞了好久好久好久好久好久才把例子跑起来.我也是服了,中间各种问题,网上的答案都尼玛一模一样!,人家都能成功,我就失败.郁闷死了.
思路
1.下载官方的服务端seata-server 2.下载官方客户端例子(我选的是springcloud-eureka-feign-mybatis-seata) 3.修改配置. 4.启动. 5.哪里错改哪里! 前4步也就半个小时,顶多俩小时,ok了. 第5步鬼知道多长时间.
我结合官方demo,整合到自己的项目中去.以及中间遇到的问题记录
seata服务端
下载seata服务端
seata服务端下载链接:https://github.com/seata/seata/releases 我选的是最新的v1.1.0版本,下载到本地即可
修改seata配置
我这里就说win下怎么启动: 首先修改目录下的conf中的配置 file.conf 其中我没有使用db方式, 使用的默认file方式,应该不需要修改
## transaction log store, only used in seata-server
store {
## store mode: file、db
mode = "file"
## file store property
file {
## store location dir
dir = "sessionStore"
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
maxBranchSessionSize = 16384
# globe session size , if exceeded throws exceptions
maxGlobalSessionSize = 512
# file buffer size , if exceeded allocate new buffer
fileWriteBufferCacheSize = 16384
# when recover batch read size
sessionReloadReadSize = 100
# async, sync
flushDiskMode = async
}
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "dbcp"
## mysql/oracle/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata"
user = "mysql"
password = "mysql"
minConn = 1
maxConn = 10
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
}
}
registry.conf 我使用的类型是eureka,配置上之前的注册中心地址
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "eureka"
eureka {
serviceUrl = "http://server01:8761/eureka/"
application = "default"
weight = "1"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "file"
file {
name = "file.conf"
}
}
启动seata服务端
修改完毕,首先启动你的注册中心eureka,然后启动这个seata服务端 首先进入bin目录 seata-server.bat 这个是win命令, 在这个目录进入cmd, 将这个文件拖进窗口,输入
-h 127.0.0.1 -p 8091 -m file
最终效果: 脚本参数: -p:指定启动seata server的端口号。 -h:指定seata server所绑定的主机 -m:指定事务日志、事务执行信息存储的方式,目前支持file(文件方式)、db(数据库方式)
这个地方我遇到的问题: 第一个:我没有指定主机,然后和后面的配置里面对应不上,显示连接不上seata server服务.
导入jar
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
修改自己项目的配置
新增两个配置文件,每个模块里面都需要,先修改一个,然后拷贝过去就行了
file.conf 文件
其中大部分是默认的,只需要修改一个地方
service {
#transaction service group mapping
vgroup_mapping.tx = "default"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "127.0.0.1:8091"
#degrade, current not support
enableDegrade = false
#disable seata
disableGlobalTransaction = false
}
注意,这个tx这个名字可以自己改,但是application.yml中一定要与之对应,往下看
完整的file.conf 文件
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
vgroup_mapping.tx = "default"
#only support when registry.type=file, please don't set multiple addresses
default.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
}
}
registry.conf文件
注册选择type = eureka,然后配置eureka地址即可
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "eureka"
nacos {
serverAddr = "localhost"
namespace = ""
cluster = "default"
}
eureka {
serviceUrl = "http://server01: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"
}
}
修改application.yml
第一个方式: 注意,这个tx-service-group: 后面的tx一定要和上面配置中的tx一样.一定一定要一样
spring:
cloud:
alibaba:
seata:
tx-service-group: tx
第二个方式:
application.yml这个文件不指定tx-service-group,那么file.conf这个文件的vgroup_mapping.tx就必须修改为seata默认的形式 在这个源码中可以看到,如果你没有定义,那么你这个file.conf配置中必须设置为applicationName + "-fescar-service-group" 举例:我这个项目就必须设置为order-fescar-service-group
spring:
application:
name: order
都是坑踩出来的..............
新增数据源配置
一开始我最下面的mybatis的配置没有注释,就会发生添加语句执行找不到mapper 注释掉就可以了.
package com.gmaya.order.config;
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
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 org.springframework.core.io.support.PathMatchingResourcePatternResolver;
/**
* 数据源代理
* @author GMaya
*/
@Configuration
public class DataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
@Primary
@Bean("dataSource")
public DataSourceProxy dataSource(DataSource druidDataSource){
return new DataSourceProxy(druidDataSource);
}
/* @Bean
public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy)throws Exception{
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:/mapper/*.xml"));
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return sqlSessionFactoryBean.getObject();
}*/
}
修改启动类
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
修改完将以上操作在pay中复制.
数据库新增表
因为我是模拟的, 所以,只要是操作数据库就行了,就没弄多个库,在一个数据库,一个表tb_user 此时在加一个seata需要的表undo_log
-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of undo_log
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;
测试
order中调用pay创建一条数据,然后自己在创建一条语句. 在order方法上加上注解
@GlobalTransactional
启动order 启动pay, 测试!
pay日志: 添加数据成功没错,但是结果回滚了 order日志: 最终数据库还是没有添加进去
再测: 调用订单-->先本地添加数据, 然后在调用支付,让支付失败,看订单是否回滚 测试结果还是回滚. 自己一步一步一步一步敲出来的,希望我下次不要遇到这些问题.