版本
- SpringBoot:2.2.5.RELEASE
- SpringCloud:Hoxton.SR3
- SpringCloudAlibaba:2.2.1.RELEASE
- Nacos Server:1.2.1
- Seata Server:1.1.0
- Jdk:1.8
部署
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服務了
- -h: 註冊到註冊中心的ip
- -p: Server rpc 監聽端口
- -m: 全局事務會話信息存儲模式,file、db,優先讀取啓動參數
- -n: Server node,多個Server時,需區分各自節點,用於生成不同區間的transactionId,以免衝突
- -e: 多環境配置參考 http://seata.io/en-us/docs/ops/multi-configuration-isolation.html
食用
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