分布式事務~seata的使用
springcloud-nacos-seata
分布式事務組件seata的使用demo,AT模式,集成nacos、springboot、springcloud、mybatis-plus,數據庫采用mysql
demo中使用的相關版本號,具體請看代碼。如果搭建個人demo不成功,驗證是否是由版本導致,由于目前這幾個項目更新比較頻繁,版本稍有變化便會出現許多奇怪問題
- seata 1.4.2
- spring-cloud-alibaba-seata 2.1.0.RELEASE
- spring-cloud-starter-alibaba-nacos-discovery 0.2.1.RELEASE
- springboot 2.0.6.RELEASE
- springcloud Finchley.RELEASE
1. 服務端配置
1.1 Nacos-server
版本為nacos-server-2.0.4,demo采用本地單機部署方式,請參考 Nacos 快速開始
1.2 Seata-server
seata-server為release版本1.4.2,demo采用本地單機部署,從此處下載 https://github.com/seata/seata/releases
并解壓
1.2.1 修改conf/registry.conf 配置
設置type、設置serverAddr為你的nacos節點地址。
注意這里有一個坑,serverAddr不能帶‘http://’前綴
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
serverAddr = "127.0.0.1"
namespace = ""
cluster = "default"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
serverAddr = "127.0.0.1"
namespace = ""
cluster = "default"
}
}
1.2.2 修改conf/nacos-config.txt 配置
service.vgroup_mapping.${your-service-gruop}=default,中間的${your-service-gruop}為自己定義的服務組名稱,服務中的application.properties文件里配置服務組名稱。
demo中有兩個服務,分別是stock-service和order-service,所以配置如下
service.vgroup_mapping.stock-service-group=default
service.vgroup_mapping.order-service-group=default
** 注意這里,高版本中應該是vgroupMapping 同時后面的如: order-service-group 不能定義為 order_service_group**
1.3 啟動seata-server
分兩步,如下
# 初始化seata 的nacos配置
cd conf
sh nacos-config.sh 127.0.0.1
# 啟動seata-server
cd bin
sh seata-server.sh -p 8091 -m file
2. 應用配置
2.1 數據庫初始化
-- 創建 order庫、業務表、undo_log表
create database seata_order;
use seata_order;
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
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;
-- 創建 stock庫、業務表、undo_log表
create database seata_stock;
use seata_stock;
DROP TABLE IF EXISTS `stock_tbl`;
CREATE TABLE `stock_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
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;
-- 初始化庫存模擬數據
INSERT INTO seata_stock.stock_tbl (id, commodity_code, count) VALUES (1, 'product-1', 9999999);
INSERT INTO seata_stock.stock_tbl (id, commodity_code, count) VALUES (2, 'product-2', 0);
2.2 應用配置
見代碼
幾個重要的配置
- 每個應用的resource里需要配置一個registry.c ,demo中與seata-server里的配置相同
- application.propeties 的各個配置項,注意spring.cloud.alibaba.seata.tx-service-group 是服務組名稱,與nacos-config.txt
配置的service.vgroup_mapping.${your-service-gruop}具有對應關系
3. 測試
-
分布式事務成功,模擬正常下單、扣庫存
localhost:9091/order/placeOrder/commit
-
分布式事務失敗,模擬下單成功、扣庫存失敗,最終同時回滾
localhost:9091/order/placeOrder/rollback
相關代碼
order模塊
/**
* 下單:創建訂單、減庫存,涉及到兩個服務
*
* @param userId
* @param commodityCode
* @param count
*/
@GlobalTransactional
@Transactional(rollbackFor = Exception.class)
public void placeOrder(String userId, String commodityCode, Integer count) {
BigDecimal orderMoney = new BigDecimal(count).multiply(new BigDecimal(5));
Order order = new Order().setUserId(userId).setCommodityCode(commodityCode).setCount(count).setMoney(
orderMoney);
orderDAO.insert(order);
stockFeignClient.deduct(commodityCode, count);
}
stock模塊
/**
* 減庫存
*
* @param commodityCode
* @param count
*/
@Transactional(rollbackFor = Exception.class)
public void deduct(String commodityCode, int count) {
if (commodityCode.equals("product-2")) {
throw new RuntimeException("異常:模擬業務異常:stock branch exception");
}
QueryWrapper<Stock> wrapper = new QueryWrapper<>();
wrapper.setEntity(new Stock().setCommodityCode(commodityCode));
Stock stock = stockDAO.selectOne(wrapper);
stock.setCount(stock.getCount() - count);
stockDAO.updateById(stock);
}
浙公網安備 33010602011771號