当前位置: 首页 > news >正文

Seata 分布式事务 快速开始

一、场景分析

假设现在有这么两个微服务 order、product,order 通过 feign 调用 product。

1.1、表结构

CREATE DATABASE IF NOT EXISTS `oms`;CREATE TABLE `oms_order` (`order_id` int NOT NULL AUTO_INCREMENT COMMENT '订单ID',`product_id` int NOT NULL COMMENT '商品ID',`quantity` int NOT NULL COMMENT '总数量',PRIMARY KEY (`order_id`)
) COMMENT='订单表';CREATE DATABASE IF NOT EXISTS `pms`;CREATE TABLE `pms_product` (`product_id` int NOT NULL AUTO_INCREMENT COMMENT '商品ID',`stock` int NOT NULL COMMENT '商品库存',`product_name` varchar(50) NOT NULL COMMENT '商品名称',PRIMARY KEY (`product_id`)
) COMMENT='商品表';INSERT INTO `pms_product` (`product_id`, `stock`, `product_name`) 
VALUES (1, 100, 'HuaWei Mate60 Pro');

 1.2、本地事务

@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {private final ProductFeignService productFeignService;@Override@Transactional(rollbackFor = Exception.class)public Order createOrder() {// 1、扣减库存PmsDeductStockDto dto = new PmsDeductStockDto();dto.setNum(1);dto.setProductId(1);productFeignService.deductStock(dto);// 2、创建订单Order order = new Order();order.setQuantity(1);order.setProductId(1);this.baseMapper.insert(order);int i = 1/0;// 报错return order;}
}

OrderServiceImpl#createOrder 加上了 @Transactional 注解,它是一个 Spring AOP 事务方法。因为 / by zero 错误,事务回滚,oms 数据库并没有插入订单数据;但是 pms 却扣减库存成功了。

pms_product 的库存 stock 从 100 被扣减到 99。

数据出现不一致:没有订单,库存却凭空减少。

这种不同数据库,或者相同数据库但不同服务之间的事务操作,所造成的数据不一致现象,叫做分布式事务问题。

我们使用 Seata 看看怎么来解决这个问题 →→

二、角色

Seata 的三大角色:

TC (Transaction Coordinator) - 事务协调者​

维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM (Transaction Manager) - 事务管理器​

定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM (Resource Manager) - 资源管理器​

管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

Seata分TC、TM和RM三个角色,TC(Server端)为单独服务端部署,TM和RM(Client端)由业务系统集成。

三、部署 Seata 服务端

3.1、版本选择

根据  Spring Cloud Alibaba 组件版本关系,到官网下载对应的 Seata 版本。

因为我的 Spring Cloud Alibaba 版本是 2021.0.5.0,所以下载 1.6.1.zip。

3.2、执行建表语句

创建数据库 seata:

CREATE DATABASE IF NOT EXISTS `seata`;

根据自己的数据库,执行下载包 seata\script\server\db 下脚本:

 

3.3、修改配置

  • 修改 application.yml

修改 seata\conf\application.yml 文件,添加配置中心与注册中心信息。

seata:config:# support: nacos, consul, apollo, zk, etcd3type: nacosnacos:server-addr: 127.0.0.1:8848group: SEATA_GROUPdata-id: seataServer.propertiesusername: nacospassword: nacosregistry:# support: nacos, eureka, redis, zk, consul, etcd3, sofatype: nacosnacos:application:  seata-serverserver-addr: 127.0.0.1:8848group: SEATA_GROUPcluster: defaultusername: nacospassword: nacos

Seata 作为分布式事务协调者,可以通过注册中心与微服务通讯。

存疑:

config.nacos.namespace 如果配置 public 会起不来,不知道其他人会不会。

  • 修改 config.txt

修改 seata\script\config-center\config.txt 文件,并将内容上传到 Nacos 配置中心 public 命名空间下,data-id 为 seataServer.properties。

#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
store.mode=db
store.lock.mode=db
store.session.mode=db#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://192.168.40.111:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123456

修改事务信息的存储方式为 db,并修改数据库连接方式。

注意 mysql 8.0 以上,使用 com.mysql.cj.jdbc.Driver 驱动。

 3.4、启动

运行 seata\bin\seata-server.bat

Nacos 服务列表有 seata-server 证明启动成功。

登录 http://localhost:7091/#/login 用户名密码 seata/seata

四、配置 Seata 客户端(AT 模式)

4.1、导入依赖

order、product 的 pom.xml 添加依赖:

<!-- seata -->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

4.2、配置 application.yml

order、product 的 application.yml 添加配置:

seata:application-id: ${spring.application.name}tx-service-group: default_tx_groupregistry:type: nacosnacos:application: seata-serverserver-addr: 127.0.0.1:8848group: SEATA_GROUPusername: nacospassword: nacosconfig:type: nacosnacos:server-addr: 127.0.0.1:8848group: SEATA_GROUPdata-id: seataServer.propertiesusername: nacospassword: nacos
  • tx-service-group 

seata 服务分组,要与服务端配置 service.vgroupMapping 的后缀对应。

  • config.nacos.data-id

新版本的 seata 服务端、客户端配置可以共用同一套。

 4.3、创建 undo_log 表(仅AT模式)

order、product 数据库创建 undo_log:

-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(`branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',`xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',`context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',`rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',`log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',`log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',`log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';

4.4、添加 @GlobalTransactional 注解

@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {private final ProductFeignService productFeignService;@Override// @Transactional(rollbackFor = Exception.class)@GlobalTransactional(name="createOrder",rollbackFor=Exception.class)public Order createOrder() {// 1、扣减库存PmsDeductStockDto dto = new PmsDeductStockDto();dto.setNum(1);dto.setProductId(1);productFeignService.deductStock(dto);// 2、创建订单Order order = new Order();order.setQuantity(1);order.setProductId(1);this.baseMapper.insert(order);int i = 1/0;return order;}
}

4.5、启动并测试

访问 localhost:8012/order/create

order-service 控制台报错:

product-service 回滚了事务:

数据库:

在文章开头从 100 减为 99 后,就没有再变化了。证明 Seata 是有效的。

http://www.xdnf.cn/news/82261.html

相关文章:

  • 动态规划算法:完全背包类问题
  • 应用案例|兵器重工:某体系需求视图模型开发
  • [论文阅读]ConfusedPilot: Confused Deputy Risks in RAG-based LLMs
  • docker容器,mysql的日志文件怎么清理
  • 【安装neo4j-5.26.5社区版 完整过程】
  • matlab设置不同颜色的柱状图
  • 华为网路设备学习-19 路由策略
  • 植物大战僵尸杂交版v3.6最新版本(附下载链接)
  • ROS 快速入门教程01
  • 加油站小程序实战教程13充值规则配置
  • 健康生活新指南
  • Java转Go日记(六):TCP黏包
  • npm i 出现permission denied
  • 树莓派学习专题<8>:使用V4L2驱动获取摄像头数据--获取摄像头支持的分辨率
  • 【Nova UI】六、SASS 赋能组件库:通用方法与混入的变革力量
  • 安宝特分享|AR智能装备赋能企业效率跃升
  • 记录一次使用面向对象的C语言封装步进电机驱动
  • IDEA热加载
  • vue3 + element-plus中el-drawer抽屉滚动条回到顶部
  • drupal7可以从测试环境一键部署到生产环境吗
  • Spring Boot 启动生命周期详解
  • WebRTC服务器Coturn服务器用户管理和安全性
  • Sentinel源码—8.限流算法和设计模式总结二
  • 机器学习06-RNN
  • 时间模块 demo
  • Ubuntu24.04安装ROS2问题
  • 【阿里云大模型高级工程师ACP学习笔记】2.2 扩展答疑机器人的知识范围
  • 深度强化学习 pdf 董豪| 马尔科夫性质,马尔科夫过程,马尔科夫奖励过程,马尔科夫决策过程
  • React:<></>的存在是为了什么
  • 【FAQ】如何配置PCoIP零客户端AWI能访问