目录
1.服务端配置
1.1.下载seata
我这里是新版本seata-service-1.7.0配置文件和旧版本不一样(下载中心 (seata.io))
1.2.配置yml
新版本是yml旧版本是config(最好搞个备份出来再配置)
配置文件application.yml(不懂的参数可以看官网Seata 参数配置)
# 服务端口号
server:
port: 7091
# 服务名称
spring:
application:
name: seata-server
# 服务日志
logging:
config: classpath:logback-spring.xml
file:
path: ../runninglogs/logs/seata
extend:
logstash-appender:
destination: 127.0.0.1:4560
kafka-appender:
bootstrap-servers: 127.0.0.1:9092
topic: logback_to_logstash
# seata可视化界面登录的账号密码这里自己设置什么启动seata,到时候就用这个来登录(http://localhost:7091/#/sagastatemachinedesigner)
console:
user:
username: seata
password: seata
# seata服务端配置
seata:
service:
vgroup-mapping:
# 前面这个是组名(liuzigu-tx-group) :后面的是对应的集群名映射名(GZ) 这两个很重要到时候客户端要设置一致
liuzigu-tx-group: GZ
disable-global-transaction: true
config:
# 可设置的配置中心: nacos, consul, apollo, zk, etcd3
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
username: nacos
password: nacos
registry:
# 可设置的注册中心: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
application: seata-server
group: SEATA_GROUP
server-addr: 127.0.0.1:8848
cluster: GZ # 这个参数在每个微服务seata时会用到这是集群参数
username: nacos
password: nacos
store:
# 可设置的数据源: file 、 db 、 redis
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/seata?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
user: root
password: 123456
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
# 将安全认证进行去除,因为可能会导致一定的问题
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
上面数据库改成自己的数据库账号密码,我这里是是mysql8.0
先直接创建一个为seata的数据库
1.3.配置seata数据库
去到我们的seata目录下找到script/server/db/mysql.sql这个文件在seata数据库执行生成数据库表
1.4.启动
这里我们已经配置好,可以启动试一试
没有报错就是启动完成,试一试是否启动成功可以去http://localhost:7091/#/sagastatemachinedesigner这里查看能不能打开这个网站
账号密码是你上面配置文件配置的账号密码
1.5.nacos配置(大坑重要!!!)
去nacos配置中心添加一个配置(重要坑!!!)
配置文件名称必须和上面application.yml里面配置的一样
然后组名必须为你配置文件的组名
还有TEXT内容也必须是你配置的内容,这个是找集群名的内容其实算是这个内容
注:这个一定要配置不然你客户端找不到集群和这个事物组名启动不了,这个配置实在是太坑了
2.客户端配置
2.1.导入依赖
<!-- seata相关的配置 这里的版本最好引入和seata下载服务版本一致的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2022.0.0.0-RC2</version>
<exclusions>
<!-- 排除原来的依赖原来的版本依赖不一定对 -->
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 我服务端是1.7.0这里就引入1.7.0 -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.7.0</version>
</dependency>
2.2.配置yml
配置客户端application.yml
# 服务端口配置
server:
port: 2003
# 应用程序配置
spring:
application:
name: seata-account-service # 服务名称
datasource: #这里被我们手动配置的数据源代替了
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/seata_account?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
username: root
password: 123456
# cloud配置
cloud:
nacos:
discovery:
server-addr: localhost:8848
group: SEATA_GROUP # 这里配置到哪个组主要看自己 这里没有强制性要求
cluster-name: GZ # 这里配置到哪个集群要看自己 这里没有强制性要求
# seata配置
seata:
tx-service-group: liuzigu-tx-group #事务组名称 必须一定和你服务端配置的一样 必须 必须 必须
service:
vgroup-mapping:
default-tx-group: GZ #事务组内容 须一定和你服务端配置的一样
# 配置中心按照这个要求配置和服务端类似
config:
type: nacos
nacos:
group: SEATA_GROUP
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
# 注册中心按照这个要求配置和服务端类似
registry:
type: nacos
nacos:
application: seata-server
server-addr: localhost:8848
username: nacos
password: nacos
cluster: GZ
group: SEATA_GROUP
# mybatis-plus配置
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:mapper/*.xml
# 暴露应用程序指标配置
management:
endpoints:
web:
exposure:
include: '*'
# 日志配置
logging:
level:
io:
seata: info
2.3.配置数据源
配置seata代理数据源(这里要配置不配置seat代理的话事物不生效)
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* @author 刘子固
* @create 2019-12-11 16:58
* 使用Seata对数据源进行代理
*/
@Configuration
public class DataSourceProxyConfig {
@Bean
public DataSource druidDataSource() {
// 其实这里的信息可以拿配置文件的,我这里写死了
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true");
druidDataSource.setUsername("root");
druidDataSource.setPassword("123456");
// 这里就是把我们德鲁伊数据源交给seata代理
DataSourceProxy dataSourceProxy = new DataSourceProxy(druidDataSource);
return dataSourceProxy;
}
}
2.4.使用
调用远程服务我这里用的是openFeign远程调用加@GlobalTransactional 注解开启分布式事物类似我们单机的Spring@Transactional注解
/**
* @author 刘子固
* @description 针对表【t_order】的数据库操作Service实现
* @createDate 2023-10-08 13:53:35
*/
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
@Autowired
AccountService accountService;
@Autowired
StorageService storageService;
@Override
@GlobalTransactional
public void create(Order order) {
System.out.println("seata全局事务id====================>"+ RootContext.getXID());
// 1.创建订单
log.info("--------------创建订单开始");
baseMapper.create(order);
// 2.扣减库存
log.info("-------------调用微服务扣减---库存");
storageService.decrease(order.getProductId(), order.getCount());
// 3.扣减余额
log.info("-------------调用微服务扣减---余额");
accountService.decrease(order.getUserId(), order.getMoney());
log.info("--------------创建订单初步结束");
// 4.修改订单状态
log.info("--------------修改订单状态");
baseMapper.updateOrderStatus(order.getUserId(), 0);
log.info("--------------修改订单状态完成");
}
}
2.5.事失效解决方案(大坑!!!)
如果用的远程调用是openFeign的话,或者事物不生效的情况下检查你其他服务的xid是不是为空或者不一致(其他远程调用框架也一样这样配置请求头加xid)
// 在订单模块服务的接口里打印
System.out.println("seata全局事务id--订单模块====================>"+ RootContext.getXID());
// 在仓库模块服务的接口里打印
System.out.println("seata全局事务id--仓库模块====================>"+ RootContext.getXID());
// 在支付模块服务的接口里打印
System.out.println("seata全局事务id--支付模块====================>"+ RootContext.getXID());
你的事物生效就是因为你远程调用的时候远程调用框架没有把xid在请求头传到对应的服务去,所以才会导致事物失效,因为想要事物生效一整个服务调用链必须保证xid是一样的。
这里openFeign远程调用的解决方法是这样的
openFeign配置类
import feign.RequestInterceptor;
import feign.RequestTemplate;
import io.seata.core.context.RootContext;
/**
* openFeign配置类
*
* @author 刘子固
* @version 1.0
* @date 2023/10/9 17:25
*/
public class MultipartSupportConfig implements RequestInterceptor {
/**
* 解决服务直接调用请求头不传递的问题
*
* @param template
*/
@Override
public void apply(RequestTemplate template) {
// 解决seata的xid未传递
String xid = RootContext.getXID();
template.header(RootContext.KEY_XID, xid);
}
}
微服务调用接口
在@FeignClient这个注解加上configuration属性然后值为刚刚的配置类这样事物不生效的问题就解决啦
import java.math.BigDecimal;
/**
* 库存微服务调用接口
*
* @author 刘子固
* @version 1.0
* @date 2023/10/8 14:18
*/
@FeignClient(name = "seata-account-service",configuration = MultipartSupportConfig.class)
public interface AccountService {
/**
* 扣余额
*
* @param userId 用户id
* @param money 金额
* @return 统一返回
*/
@PostMapping(value = "/account/decrease")
CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}
好了教程到这里就结束了,有什么不理解的地方可以私信博主哦!
给个关注吧!!!