seata:Simple Extensible Autonomous Transaction Architecture
中文官方:http://seata.io/zh-cn/
中文wiki:https://github.com/seata/seata
2019 年 1 月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And Rollback),和社区一起共建开源分布式事务解决方案。Fescar 的愿景是让分布式事务的使用像本地事务的使用一样,简单和高效,并逐步解决开发者们遇到的分布式事务方面的所有难题。
Fescar 开源后,蚂蚁金服加入 Fescar 社区参与共建,并在 Fescar 0.4.0 版本中贡献了 TCC 模式。
为了打造更中立、更开放、生态更加丰富的分布式事务开源社区,经过社区核心成员的投票,大家决定对 Fescar 进行品牌升级,并更名为 Seata,意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案。
Seata 融合了阿里巴巴和蚂蚁金服在分布式事务技术上的积累,并沉淀了新零售、云计算和新金融等场景下丰富的实践经验,但要实现适用于所有的分布式事务场景的愿景,仍有很长的路要走。因此,我们决定建立一个完全中立的分布式事务组织,希望更多的企业、开发者能够加入我们,一起打造 Seata。
历史:
Ant Financial
XTS:Extended Transaction Service,可扩展事务服务。蚂蚁金服中间件团队自2007年以来开发了分布式事务中间件,广泛应用于Ant Financial,解决了跨数据库和服务的数据一致性问题。
DTX:Distributed Transaction Extended。自2013年以来,XTS已在Ant Financial Cloud上发布,名称为DTX。
阿里巴巴
TXC:Taobao Transaction Constructor。阿里巴巴中间件团队自2014年起启动该项目,以解决因应用程序架构从单片机改为微服务而导致的分布式事务问题。
GTS:Global Transaction Service。 TXC作为Aliyun中间件产品,新名称GTS自2016年起发布。
Fescar:我们从2019年开始基于TXC / GTS开源开源项目Fescar,以便在未来与社区密切合作。
Seata社区
Seata:简单的可扩展自治交易架构。 Ant Financial加入Fescar,使其成为一个更加中立和开放的分布式服务社区,并将Fescar更名为Seata。
Seata有3个基本组件:
Seata管理分布式事务的典型生命周期:
至此,seata的协议机制总体上看与 XA 是一致的。但是是有差别的:
XA 方案的 RM 实际上是在数据库层,RM 本质上就是数据库自身(通过提供支持 XA 的驱动程序来供应用使用)。
而 Fescar 的 RM 是以二方包的形式作为中间件层部署在应用程序这一侧的,不依赖于数据库本身对协议的支持,当然也不需要数据库支持 XA 协议。这点对于微服务化的架构来说是非常重要的:应用层不需要为本地事务和分布式事务两类不同场景来适配两套不同的数据库驱动。
这个设计,剥离了分布式事务方案对数据库在 协议支持 上的要求。
springCloud整合seata:https://github.com/seata/seata-samples/tree/master/springcloud-jpa-seata
继续之前的细分商品保存, 现在同时要保存库存信息及优惠套餐信息
请求菜单 : 发布商品
请求url : /goods/goodsskuinfo-step-all
路径 : \src\views\modules\goods\goodsskuinfo-step-all.vue
goodsskuinfo-step-all 完整 页面
上一步
下一步
立即创建
基本信息
选择商品
供应商
当前价格
产品属性
库存信息
优惠套餐
套餐总数量
套餐明细
套餐优惠
选择细分商品
取消
路径 : \src\views\modules\common\goods-sku-many-dialog.vue
查询
请求url : /goods/goodsskuinfo/getDataListNotIds
模块 : five-mall-goods
Controller
路径 : com.yuan.goods.controller.GoodsSkuInfoController
@RequestMapping("/getDataListNotIds")
public R getDataListNotIds(@RequestParam Map<String, Object> params){
System.out.println("params.get(\"ids\") = " + params.get("ids"));
PageUtils page = goodsSkuInfoService.queryPageNotIds(params);
return R.ok().put("page", page);
}
Service
路径 : com.yuan.goods.service.GoodsSkuInfoService
PageUtils queryPageNotIds(Map<String, Object> params);
ServiceImpl
路径 : com.yuan.goods.service.impl.GoodsSkuInfoServiceImpl
@Override
public PageUtils queryPageNotIds(Map<String, Object> params) {
String[] ids = params.get("ids").toString().split(",");
IPage<GoodsSkuInfoEntity> page = this.page(
new Query<GoodsSkuInfoEntity>().getPage(params),
new LambdaQueryWrapper<GoodsSkuInfoEntity>()
.like(GoodsSkuInfoEntity::getSkuTitle, params.get("key"))
.notIn(GoodsSkuInfoEntity::getSkuId,ids )
);
return new PageUtils(page);
}
请求url : /stock/repositoryinfo/listAll
模块 : five-mall-stock
Controller
路径 : com.yuan.stock.controller.RepositoryInfoController
@RequestMapping("/listAll")
public R listAll(){
List<RepositoryInfoEntity> list = repositoryInfoService.list();
return R.ok().put("list", list);
}
模块 : five-mall-goods
路径 : com.yuan.goods.vo.SkuManyVo
package com.yuan.goods.vo;
import lombok.Data;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Data
public class SkuManyVo {
// sku
private Integer skuId;
private Integer goodsId;
private Integer supplierId;
private String skuTitle;
private Double skuPrice;
private String skuInfo;
private Date modifiedTime;
private Double skuNowPrice;
private String goodsName;
private String supplierName;
// attribute
private List<Integer> attrTypeIdList = new ArrayList<>();
private List<String> attrTypeNameList = new ArrayList<>();
private List<String> attrValueList = new ArrayList<>();
private List<String> attrValueUnitList = new ArrayList<>();
private List<String> attrValueInfoList = new ArrayList<>();
// stock
private Date createDate;
private List<Integer> repositoryIdList = new ArrayList<>();
private List<String> repositoryNameList = new ArrayList<>();
private List<Integer> stockCountList = new ArrayList<>();
private List<Integer> stockWarnMaxList = new ArrayList<>();
private List<Integer> stockWarnMinList = new ArrayList<>();
// package
private Integer packageId;
private String packageInfo;
private Double packageTotal;
private Double packageDiscount;
private String packageTitle;
private String packageCode;
private Integer packageCount;
private Date packageInTime;
private Date packageBeginDate;
private String packageState;
// packageDetail
private List<Integer> skuIdList = new ArrayList<>();
private List<Integer> packageDetailCountList = new ArrayList<>();
private List<Double> packageDetailPriceList = new ArrayList<>();
private List<String> skuTitleList = new ArrayList<>();
private List<Double> skuNowPriceList = new ArrayList<>();
private List<String> masterImagePathList = new ArrayList<>();
}
模块 : five-mall-goods
路径 : com.yuan.goods.service.impl.GoodsSkuInfoServiceImpl
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveMany(SkuManyVo skuManyVo) {
GoodsSkuInfoEntity goodsSkuInfo = null;
boolean save = false;
// 保存 sku
goodsSkuInfo = new GoodsSkuInfoEntity();
goodsSkuInfo.setGoodsId(skuManyVo.getGoodsId());
goodsSkuInfo.setSupplierId(skuManyVo.getSupplierId());
goodsSkuInfo.setSkuTitle(skuManyVo.getSkuTitle());
goodsSkuInfo.setSkuPrice(skuManyVo.getSkuPrice());
goodsSkuInfo.setSkuInfo(skuManyVo.getSkuInfo());
goodsSkuInfo.setModifiedTime(new Date());
goodsSkuInfo.setSkuNowPrice(skuManyVo.getSkuNowPrice());
goodsSkuInfo.setGoodsName(skuManyVo.getGoodsName());
goodsSkuInfo.setSupplierName(skuManyVo.getSupplierName());
goodsSkuInfo.setSaleCount(0);
System.out.println("goodsSkuInfo = " + goodsSkuInfo);
save = this.save(goodsSkuInfo);
if (save) {
// 保存 attributeValue
List<GoodsAttrValueEntity> goodsAttrValueEntityList = new ArrayList<>();
if (skuManyVo.getAttrTypeIdList() != null) {
for (int i = 0; i < skuManyVo.getAttrTypeIdList().size(); i++) {
if(skuManyVo.getAttrValueList().get(i)!=null && !"".equals(skuManyVo.getAttrValueList().get(i))){
GoodsAttrValueEntity goodsAttrValueEntity = new GoodsAttrValueEntity();
goodsAttrValueEntity.setAttrTypeId(skuManyVo.getAttrTypeIdList().get(i));
goodsAttrValueEntity.setSkuId(goodsSkuInfo.getSkuId());
goodsAttrValueEntity.setAttrValue(skuManyVo.getAttrValueList().get(i));
goodsAttrValueEntity.setAttrValueUnit(skuManyVo.getAttrValueUnitList().get(i));
goodsAttrValueEntity.setAttrValueInfo(skuManyVo.getAttrValueInfoList().get(i));
goodsAttrValueEntity.setAttrTypeName(skuManyVo.getAttrTypeNameList().get(i));
goodsAttrValueEntity.setSkuTitle(goodsSkuInfo.getSkuTitle());
System.out.println("goodsAttrValueEntity = " + goodsAttrValueEntity);
goodsAttrValueEntityList.add(goodsAttrValueEntity);
}
}
}
goodsAttrValueService.saveBatch(goodsAttrValueEntityList);
// 保存 stock
List<GoodsStockInfoVo> goodsStockInfoVoList = new ArrayList();
if (skuManyVo.getRepositoryIdList() != null) {
for (int i = 0; i < skuManyVo.getRepositoryIdList().size(); i++) {
GoodsStockInfoVo goodsStockInfoVo = new GoodsStockInfoVo();
goodsStockInfoVo.setRepositoryId(skuManyVo.getRepositoryIdList().get(i));
goodsStockInfoVo.setSkuId(goodsSkuInfo.getSkuId());
goodsStockInfoVo.setStockCount(skuManyVo.getStockCountList().get(i));
goodsStockInfoVo.setStockWarnMax(skuManyVo.getStockWarnMaxList().get(i));
goodsStockInfoVo.setStockWarnMin(skuManyVo.getStockWarnMinList().get(i));
goodsStockInfoVo.setStockWarnState("1");
goodsStockInfoVo.setStockInPrice(goodsSkuInfo.getSkuNowPrice());
goodsStockInfoVo.setStockSn("");
goodsStockInfoVo.setCreateDate(skuManyVo.getCreateDate());
goodsStockInfoVo.setStockInTime(new Date());
goodsStockInfoVo.setRepositoryName(skuManyVo.getRepositoryNameList().get(i));
goodsStockInfoVo.setSkuTitle(goodsSkuInfo.getSkuTitle());
System.out.println("goodsStockInfoVo = " + goodsStockInfoVo);
goodsStockInfoVoList.add(goodsStockInfoVo);
}
String stockData = JSONObject.toJSONString(goodsStockInfoVoList);
stockFeignService.saveBatchForSku(stockData);
}
// 保存 package
GoodsPackageVo goodsPackageVo = new GoodsPackageVo();
goodsPackageVo.setSkuId(goodsSkuInfo.getSkuId());
goodsPackageVo.setPackageInfo(skuManyVo.getPackageInfo());
goodsPackageVo.setPackageTotal(skuManyVo.getPackageTotal());
goodsPackageVo.setPackageDiscount(skuManyVo.getPackageDiscount());
goodsPackageVo.setPackageTitle(skuManyVo.getPackageTitle());
goodsPackageVo.setPackageCode(skuManyVo.getPackageCode());
goodsPackageVo.setPackageCount(skuManyVo.getPackageCount());
goodsPackageVo.setPackageInTime(new Date());
goodsPackageVo.setPackageBeginDate(skuManyVo.getPackageBeginDate());
goodsPackageVo.setPackageState("1");
goodsPackageVo.setSkuTitle(goodsSkuInfo.getSkuTitle());
goodsPackageVo.setSkuIdList(skuManyVo.getSkuIdList());
goodsPackageVo.setPackageDetailCountList(skuManyVo.getPackageDetailCountList());
goodsPackageVo.setPackageDetailPriceList(skuManyVo.getPackageDetailPriceList());
goodsPackageVo.setMasterImagePathList(skuManyVo.getMasterImagePathList());
goodsPackageVo.setSkuNowPriceList(skuManyVo.getSkuNowPriceList());
goodsPackageVo.setSkuTitleList(skuManyVo.getSkuTitleList());
System.out.println("goodsPackageVo = " + goodsPackageVo);
R packageR = couponFeignService.saveManyForSku(goodsPackageVo);
System.out.println("packageR = " + packageR);
}
return save;
}
路径 : com.yuan.goods.feign.StockFeignService
/**
* 批量保存库存
*/
@RequestMapping("/stock/goodsstockinfo/saveBatchForSku")
public R saveBatchForSku(@RequestBody String datas);
模块 : five-mall-stock
controller
路径 : com.yuan.stock.controller.GoodsStockInfoController
@RequestMapping("/saveBatchForSku")
public R saveBatchForSku(@RequestBody String datas){
System.out.println("datas = " + datas);
List<GoodsStockInfoEntity> goodsStockInfoEntities = JSONObject.parseObject(datas, new TypeReference<List<GoodsStockInfoEntity>>() {
});
System.out.println("goodsStockInfoEntities = " + goodsStockInfoEntities);
boolean b = goodsStockInfoService.saveBatchForSku(goodsStockInfoEntities);
if (b) {
return R.ok();
}else {
return R.error();
}
}
service
路径 : com.yuan.stock.service.GoodsStockInfoService
boolean saveBatchForSku(List<GoodsStockInfoEntity> goodsStockInfoEntities);
serviceImpl
**路径 : **com.yuan.stock.service.impl.GoodsStockInfoServiceImpl
@Override
public boolean saveBatchForSku(List<GoodsStockInfoEntity> goodsStockInfoEntities) {
return this.saveBatch(goodsStockInfoEntities);
}
路径 : com.yuan.goods.feign.CouponFeignService
package com.yuan.goods.feign;
import com.yuan.common.utils.R;
import com.yuan.goods.vo.GoodsPackageVo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient("five-mall-coupon")
public interface CouponFeignService {
@RequestMapping("/coupon/goodspackage/saveManyForSku")
public R saveManyForSku(@RequestBody GoodsPackageVo goodsPackageVo);
}
模块 : five-mall-coupon
controller
路径 : com.yuan.coupon.controller.GoodsPackageController
@RequestMapping("/saveManyForSku")
public R saveManyForSku(@RequestBody GoodsPackageVo goodsPackageVo){
boolean r = goodsPackageService.saveManyForSku(goodsPackageVo);
if (r) {
return R.ok();
}else{
return R.error();
}
}
vo传值类
路径 : com.yuan.coupon.vo.GoodsPackageVo
package com.yuan.coupon.vo;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 销售促销套餐 fmp_goods_package
*
* @author yuan
* @email [email protected]
* @date 2022-04-06 16:26:24
*/
@Data
public class GoodsPackageVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 套餐主键
*/
private Integer packageId;
/**
* 细分商品 : goods_sku_info 外键
*/
private Integer skuId;
/**
* 套餐描述
*/
private String packageInfo;
/**
* 套餐总价
*/
private Double packageTotal;
/**
* 套餐优惠
*/
private Double packageDiscount;
/**
* 套餐标题
*/
private String packageTitle;
/**
* 套餐代号
*/
private String packageCode;
/**
* 套餐总数量
*/
private Integer packageCount;
/**
* 录入时间
*/
private Date packageInTime;
/**
* 启用时间
*/
private Date packageBeginDate;
/**
* 套餐状态 : 0 下线 1 使用
*/
private String packageState;
/**
* 商品标题
*/
private String skuTitle;
// packageDetail
private List<Integer> skuIdList = new ArrayList<>();
private List<Integer> packageDetailCountList = new ArrayList<>();
private List<Double> packageDetailPriceList = new ArrayList<>();
private List<String> skuTitleList = new ArrayList<>();
private List<Double> skuNowPriceList = new ArrayList<>();
private List<String> masterImagePathList = new ArrayList<>();
}
service
路径 : com.yuan.coupon.service.GoodsPackageService
boolean saveManyForSku(GoodsPackageVo goodsPackageVo);
serviceImpl
**路径 *com.yuan.coupon.service.impl.GoodsPackageServiceImpl
@Override
public boolean saveManyForSku(GoodsPackageVo goodsPackageVo) {
GoodsPackageEntity goodsPackageEntity = new GoodsPackageEntity();
BeanUtils.copyProperties(goodsPackageVo, goodsPackageEntity);
System.out.println("goodsPackageEntity = " + goodsPackageEntity);
boolean save = this.save(goodsPackageEntity);
if (save) {
List<GoodsPackageDetailEntity> goodsPackageDetailEntitieList = new ArrayList<>();
for (int i = 0; i < goodsPackageVo.getSkuIdList().size(); i++) {
GoodsPackageDetailEntity goodsPackageDetailEntity = new GoodsPackageDetailEntity();
goodsPackageDetailEntity.setSkuId(goodsPackageVo.getSkuIdList().get(i));
goodsPackageDetailEntity.setPackageId(goodsPackageEntity.getPackageId());
goodsPackageDetailEntity.setPackageDetailCount(goodsPackageVo.getPackageDetailCountList().get(i));
goodsPackageDetailEntity.setPackageDetailPrice(goodsPackageVo.getPackageDetailPriceList().get(i));
goodsPackageDetailEntity.setSkuTitle(goodsPackageVo.getSkuTitleList().get(i));
goodsPackageDetailEntity.setPackageCode(goodsPackageEntity.getPackageCode());
goodsPackageDetailEntity.setMasterImagePath(goodsPackageVo.getMasterImagePathList().get(i));
goodsPackageDetailEntity.setSkuNowPrice(goodsPackageVo.getSkuNowPriceList().get(i));
System.out.println("goodsPackageDetailEntity = " + goodsPackageDetailEntity);
goodsPackageDetailEntitieList.add(goodsPackageDetailEntity);
}
goodsPackageDetailService.saveBatch(goodsPackageDetailEntitieList);
}
return save;
}
下载地址:https://github.com/seata/seata/releases
路径 : \seata-server-1.3.0\seata\bin
seata-server.bat –m file
-m 事务日志存储方式,支持file,db,redis等 默认file
Server端存储模式(store.mode)现有file、db、redis三种(后续将引入raft,mongodb),file模式无需改动,直接启动即可,下面专门讲下db和redis启动步骤。
注: file模式为单机模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高;
db模式为高可用模式,全局事务会话信息通过db共享,相应性能差些;
redis模式Seata-Server 1.3及以上版本支持,性能较高,存在事务信息丢失风险,请提前配置合适当前场景的redis持久化配置.
可以看到 Server started 启动成功
i.s.core.rpc.netty.NettyServerBootstrap : Server started, listen port: 8091
路径 : \seata-server-1.3.0\seata\conf\ file.conf
修改数据库连接
file.conf 30行
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.cj.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata_server"
user = "root"
password = "root"
minConn = 5
maxConn = 30
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
恢复 seata_server 数据库
/*
各个表对应功能:
全局事务---global_table
分支事务---branch_table
全局锁-----lock_table
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for branch_table
-- ----------------------------
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table` (
`branch_id` bigint(20) NOT NULL,
`xid` varchar(128) NOT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`resource_group_id` varchar(32) DEFAULT NULL,
`resource_id` varchar(256) DEFAULT NULL,
`lock_key` varchar(128) DEFAULT NULL,
`branch_type` varchar(8) DEFAULT NULL,
`status` tinyint(4) DEFAULT NULL,
`client_id` varchar(64) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of branch_table
-- ----------------------------
-- ----------------------------
-- Table structure for global_table
-- ----------------------------
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table` (
`xid` varchar(128) NOT NULL,
`transaction_id` bigint(20) DEFAULT NULL,
`status` tinyint(4) NOT NULL,
`application_id` varchar(32) DEFAULT NULL,
`transaction_service_group` varchar(32) DEFAULT NULL,
`transaction_name` varchar(128) DEFAULT NULL,
`timeout` int(11) DEFAULT NULL,
`begin_time` bigint(20) DEFAULT NULL,
`application_data` varchar(2000) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of global_table
-- ----------------------------
-- ----------------------------
-- Table structure for lock_table
-- ----------------------------
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table` (
`row_key` varchar(128) NOT NULL,
`xid` varchar(96) DEFAULT NULL,
`transaction_id` mediumtext,
`branch_id` mediumtext,
`resource_id` varchar(256) DEFAULT NULL,
`table_name` varchar(32) DEFAULT NULL,
`pk` varchar(36) DEFAULT NULL,
`gmt_create` datetime DEFAULT NULL,
`gmt_modified` datetime DEFAULT NULL,
PRIMARY KEY (`row_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of lock_table
-- ----------------------------
每一个分库 增加 操作日志表 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;
在common 模块里 增加对seata 支持
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-seataartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${spring-cloud-alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<spring-cloud-alibaba.version>2.2.2.RELEASEspring-cloud-alibaba.version>
将当前服务 注册到 seata上的配置
spring.cloud.alibaba.seata.tx-service-group=default
seata.application-id=${spring.application.name}
seata.service.vgroupMapping.default=default
路径 : \seata-server-1.3.0\seata\conf\registry.conf
#注册
nacos {
application = "serverAddr"
serverAddr = "127.0.0.1:8848"
group = "DEFAULT_GROUP"
namespace = "public"
cluster = "default"
username = ""
password = ""
}
#...
#配置
nacos {
serverAddr = "127.0.0.1:8848"
namespace = "public"
group = "default"
username = ""
password = ""
}
设置 注册平台 nacos , 并复制registry.conf 到每一个模块 resources 文件夹下
这样每一个服务 就能找到 seata 服务
在每个模块 config 包下 seata 的datasource
import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
/**
* @Description TODO
* @Version 1.0
**/
@Configuration
public class MySeataConfig {
@Autowired
DataSourceProperties dataSourceProperties;
@Bean
public DataSource dataSource(DataSourceProperties dataSourceProperties) {
HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
if (StringUtils.hasText(dataSourceProperties.getName())) {
dataSource.setPoolName(dataSourceProperties.getName());
}
return new DataSourceProxy(dataSource);
}
}
同时修改 启动类注解, 取消数据源自动配置
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
用注解保证分布式注解
@GlobalTransactional
模块 : five-mall-goods
**路径 : **com.yuan.goods.service.impl.GoodsSkuInfoServiceImpl
@GlobalTransactional
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveMany(SkuManyVo skuManyVo) {
...
}
@Transactional(rollbackFor = Exception.class)
模块 : five-mall-goods
**路径 : **com.yuan.goods.service.impl.GoodsSkuInfoServiceImpl
@GlobalTransactional
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveMany(SkuManyVo skuManyVo) {
...
}
模块 : five-mall-coupon
**路径 *com.yuan.coupon.service.impl.GoodsPackageServiceImpl
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveManyForSku(GoodsPackageVo goodsPackageVo) {
...
}
模块 : five-mall-stock
**路径 : **com.yuan.stock.service.impl.GoodsStockInfoServiceImpl
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveBatchForSku(List<GoodsStockInfoEntity> goodsStockInfoEntities) {
//System.out.println("stock 出异常:" + 1/0);
return this.saveBatch(goodsStockInfoEntities);
}