0:下载Nacos Server和Seata Server
https://github.com/alibaba/nacos/releases
https://github.com/seata/seata/releases
1:部署Nacos Server
Nacos快速开始:https://blog.csdn.net/momo57l/article/details/104298206
Nacos集群部署:https://blog.csdn.net/momo57l/article/details/104737165
2:部署Seata Server
第一步:修改conf/registry.conf:注册中心和配置中心都改为使用nacos,serverAddr填nacos地址,namespace是nacos中的命名空间,默认不填是default,config下的group是nacos中的分组,默认是SEATA_GROUP,也可以改成其他分组
registry {
type = "nacos"
nacos {
serverAddr = "localhost"
namespace = "90f980dc-c419-4be2-95a2-85d5c73deccb"
cluster = "default"
}
}
config {
type = "nacos"
nacos {
serverAddr = "localhost"
namespace = "90f980dc-c419-4be2-95a2-85d5c73deccb"
group = "SEATA_GROUP"
}
}
第二步:下载所需脚本文件:都在github script文件夹下,需要下载维护seata事务信息的mysql.sql、seata配置文件config.txt和将seata配置文件导入nacos的导入脚本
下载完成后将会得到三个文件:mysql.sql、config.txt和nacos-config.sh(或者nacos-config.py)
第三步:修改config.txt
# 将存储seata事务信息的方式改为db存储
store.mode=db
# 改为新建或者现有数据库配置信息
store.db.datasource=dbcp
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/db_seata_0420?useUnicode=true
store.db.user=root
store.db.password=root
store.db.minConn=1
store.db.maxConn=3
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
第四步:执行mysql.sql:在第三步配置的数据库中执行SQL文件后得到三张表,分别是lock_table、branch_table、global_table
第五步:将config.txt导入至nacos:我这里是window环境,所以使用的是nacos-config.py这个脚本,在控制台执行python .\nacos-config.py localhost:8848即可完成导入,Linux环境可以使用nacos-config.sh脚本,导入过程有问题的话建议先打开导入脚本看下config.txt目录是否正确。数据导入到nacos后是在默认的命名空间
第六步:将导入至nacos中的config.txt信息迁移至对应的命名空间(namespace):在第一步中registry.conf文件里填写了对应的namespace,如果是默认的则可以跳过该步骤。迁移方式是先导出再到对应的命名空间中执行导入,然后删除之前命名空间中的配置信息即可,注意要将SEATA_GROUP的配置信息全部勾选
第七步:启动Seata Server:windows环境下直接在控制台执行.\seata-server.bat即可启动。Linux环境下可以使用seata-server.sh -h 127.0.0.1 -p 8091 -m db -n 1 -e test指令启动。启动完成后在nacos服务列表中就可以看到seata服务了
0:项目搭建
第一步:引入相关依赖,包括jpa、nacos、seata、web等
# nacos只需要服务发现的客户端,如果要使用配置中心的话可以再引入nacos-config
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
# seata客户端依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
第二步:创建两个微服务,分别是demo-orde(订单服务)和demo-payment(支付服务),支付服务中有创建支付单业务,订单服务中有创建订单业务,创建订单操作会通过feign client调用支付服务中的创建支付单业务,两个业务分别处于各自本地事务中
订单服务中的创建订单
/**
* 创建订单业务,会通过feign client调用支付服务中的创建支付单业务
* 为了方便测试,orderBo中有个flag字段,可以控制下面这个异常是否执行
*/
@GlobalTransactional
@Transactional(rollbackOn = Exception.class)
public void createOrder(OrderBO orderBo) {
OrderPO po = new OrderPO();
po.setOrderCode("TEST" + System.currentTimeMillis());
po.setOrderAmount(orderBo.getOrderAmount());
orderRepository.save(po);
paymentFeignClient.createOrderPayment(OrderPaymentBO.builder()
.orderId(po.getId())
.orderAmount(orderBo.getOrderAmount())
.build());
if (orderBo.getFlag()) {
throw new RuntimeException("demo order exception.");
}
}
支付服务中的创建支付单业务
/**
* 支付服务中的创建支付单业务
*/
@Transactional(rollbackOn = Exception.class)
public void createOrderPayment(OrderPaymentBO orderPaymentBo) {
OrderPaymentPO po = new OrderPaymentPO();
po.setOrderId(orderPaymentBo.getOrderId());
po.setOrderAmount(orderPaymentBo.getOrderAmount());
orderPaymentRepository.save(po);
}
主要配置信息,注意nacos配置下的namespace要跟之前配置的namespace一致。seata配置下的tx-service-group需要手动指定,其中service.vgroup-mapping.${tx-service-group}配置成default
server:
port: 8095
seata:
tx-service-group: ${spring.application.name}
service:
vgroup-mapping:
demo-order: default
spring:
application:
name: demo-order
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 90f980dc-c419-4be2-95a2-85d5c73deccb
第三步:测试,如果两个业务分别处于各自本地事务,在创建订单业务中,如果通过feign client调用支付服务之后发生异常的话,支付服务的业务是不会回滚的
POST localhost:8095/order/create-order
{
"orderAmount":100,
"flag":true
}
结果跟预期一致,订单表没有新增记录,支付单新增了一条记录
第四步:引入seata,只需要在订单业务上加上@GlobalTransactional注解即可,这时两个业务将处于同一个全局事务中,如果订单调用支付后发生异常,则两者都会回滚
@GlobalTransactional
@Transactional(rollbackOn = Exception.class)
public void createOrder(OrderBO orderBo) {
OrderPO po = new OrderPO();
po.setOrderCode("TEST" + System.currentTimeMillis());
po.setOrderAmount(orderBo.getOrderAmount());
orderRepository.save(po);
paymentFeignClient.createOrderPayment(OrderPaymentBO.builder()
.orderId(po.getId())
.orderAmount(orderBo.getOrderAmount())
.build());
if (orderBo.getFlag()) {
throw new RuntimeException("demo order exception.");
}
}
此时异常发生后,两个业务都会回滚
补充:SEATA AT 模式需要 UNDO_LOG 表支持,所有我们需要在业务数据库中新增一张UNDO_LOG 表
-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) 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) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Seata:http://seata.io/zh-cn/
Spring Cloud Alibaba:https://spring.io/projects/spring-cloud-alibaba
Gitee:https://gitee.com/liujiazhongg_admin/seata-nacos-example