springCloud/Alibaba常用中间件之Seata分布式事务
文章目录
- SpringCloud Alibaba:
- 依赖版本补充
- Seata处理分布式事务(AT模式)
- AT模式介绍
- 核心组件介绍
- AT的工作流程:两阶段提交(**2PC**)
- Seata-AT模式使用
- Seata(2.0.0)下载、配置和启动
- Seata案例实战
- 前置代码
- 添加全局注解 @GlobalTransactional
SpringCloud Alibaba:
官方学习文档(中文): https://spring-cloud-alibaba-group.github.io/github-pages/2022/zh-cn/2022.0.0.0-RC2.html
微服务的中间件介绍与使用
微服务架构体系图:
依赖版本补充
下面所有代码中的依赖版本如下:
<properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><hutool.version>5.8.22</hutool.version><lombok.version>1.18.26</lombok.version><druid.version>1.1.20</druid.version><mybatis.springboot.version>3.0.2</mybatis.springboot.version><mysql.version>8.0.11</mysql.version><swagger3.version>2.2.0</swagger3.version><mapper.version>4.2.3</mapper.version><fastjson2.version>2.0.40</fastjson2.version><persistence-api.version>1.0.2</persistence-api.version><spring.boot.test.version>3.1.5</spring.boot.test.version><spring.boot.version>3.2.0</spring.boot.version><spring.cloud.version>2023.0.0</spring.cloud.version><spring.cloud.alibaba.version>2023.0.0.0-RC1</spring.cloud.alibaba.version><knife4j-openapi3.version>4.4.0</knife4j-openapi3.version> </properties>
Seata处理分布式事务(AT模式)
Seata,是一款开源的分布式事务 解决方案,其中有四种模式来去实现分布式事务:
- AT(Auto Transaction):基于两阶段提交(2PC)的自动模式,通过代理数据源自动记录事务前后的数据快照(UNDO_LOG),实现自动回滚。无代码侵入,适合大部分业务场景。
- TCC(Try-Confirm-Cancel):需手动编写 Try(资源预留)、Confirm(提交)、Cancel(回滚)三个接口,适用于对一致性要求极高或业务逻辑复杂的场景(如资金交易)。
- Saga:长事务解决方案,通过状态机编排服务调用链,每个步骤提供正向操作和补偿操作,适合跨多个服务的长时间事务(如电商订单流程)。
- XA:基于数据库的 XA 协议实现强一致性,依赖数据库自身的事务能力,适合传统 XA 兼容场景。
其作用就是在应对一个服务请求中存在多个数据库的操作时,为了确保数据的 一致性 会对此进行分布式事务处理,
当某一个操作报错或者超时时就进行回滚,若是程序运行正常便直接提交。避免了对数据库不一致的问题,这里只以AT模式作为演示,要是想了解其他的模式可以去查看官网
AT模式介绍
在开始介绍之前先声明一下,seata的AT模式使用起来并不难,甚至只需要添加一个注释即可,
但是在面试中seata的AT模式会问一些关于概念性的东西所以这里详细的介绍一下,
若是你只想要知道如何使用直接向下看到Seata-AT模式使用
核心组件介绍
在Seata中四种模式下都会有的三个 核心组件 (TC、TM、RM):
- TC(Transaction Coordinator[事务协调器]):
负责全局事务的 提交 和 回滚,- TM(Transaction Manage[事务管理]):
定义事务的边界(如@GlobalTransactional
),事务的发起者。(如何实现管理:会对此事务进行生成一个 XID,用来区分全局事务和定义边界)- RM(Resource Manage[资源管理]):
资源管理器,管理分支事务,与 TC 通信上报状态并执行指令。(如何实现管理:会对每一个分支生成一个branchId)
架构图展示来自官网:
AT的工作流程:两阶段提交(2PC)
在 AT模式 下事务的提交/回滚,是由两个阶段完成的:
- 第一阶段:
对数据的插入和更新等操作分别记录数据在 操作前(beforeImage) 和 操作后(afterImage) 的数据,
并将branchId、XID、undoItems{beforeImage{rows、tableName}、afterImage{rows、tableName}}
插入到 UNDO_LOG 表中对下一阶段做准备,
同时将业务数据也进行提交。
- 第二阶段:
对TC下的分支提供的消息进行判断是要Commit(提交) 还是 Rollbacked(回滚),
- Commit(提交):提交第一阶段的 操作后(afterImage) 的数据进行 Commit(提交),然后在将 UNDO_LOG 中的数据进行删除
- Rollbacked(回滚):开启一个事务进行数据(本地数据库与UNDO_LOG的afterImage)的比较,
- 一致:说明数据未被其他事务修改,允许回滚。
- 不一致:说明数据已被其他事务修改(脏写),回滚失败,需人工介入处理。
这一部分是个人参考官网的理解进行概述可能会有一些表达并不到位,大家可以到官网进行参考
Seata-AT模式使用
Seata(2.0.0)下载、配置和启动
- 下载地址:Seata的官网,找到Seata(2.0.0)的版本,下载资源
seata-server-2.0.0.zip
,即可- Seata的配置:要想使用Seata模式的话还要进行Seata的专属库的创建,
-- -------------------------------- The script used when storeMode is 'db' -------------------------------- -- the table to store GlobalSession data CREATE DATABASE seata; USE seata; CREATE TABLE IF NOT EXISTS `global_table` (`xid` VARCHAR(128) NOT NULL,`transaction_id` BIGINT,`status` TINYINT NOT NULL,`application_id` VARCHAR(32),`transaction_service_group` VARCHAR(32),`transaction_name` VARCHAR(128),`timeout` INT,`begin_time` BIGINT,`application_data` VARCHAR(2000),`gmt_create` DATETIME,`gmt_modified` DATETIME,PRIMARY KEY (`xid`),KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),KEY `idx_transaction_id` (`transaction_id`) ) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;-- the table to store BranchSession data CREATE TABLE IF NOT EXISTS `branch_table` (`branch_id` BIGINT NOT NULL,`xid` VARCHAR(128) NOT NULL,`transaction_id` BIGINT,`resource_group_id` VARCHAR(32),`resource_id` VARCHAR(256),`branch_type` VARCHAR(8),`status` TINYINT,`client_id` VARCHAR(64),`application_data` VARCHAR(2000),`gmt_create` DATETIME(6),`gmt_modified` DATETIME(6),PRIMARY KEY (`branch_id`),KEY `idx_xid` (`xid`) ) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;-- the table to store lock data CREATE TABLE IF NOT EXISTS `lock_table` (`row_key` VARCHAR(128) NOT NULL,`xid` VARCHAR(128),`transaction_id` BIGINT,`branch_id` BIGINT NOT NULL,`resource_id` VARCHAR(256),`table_name` VARCHAR(32),`pk` VARCHAR(36),`status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',`gmt_create` DATETIME,`gmt_modified` DATETIME,PRIMARY KEY (`row_key`),KEY `idx_status` (`status`),KEY `idx_branch_id` (`branch_id`),KEY `idx_xid` (`xid`) ) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;CREATE TABLE IF NOT EXISTS `distributed_lock` (`lock_key` CHAR(20) NOT NULL,`lock_value` VARCHAR(20) NOT NULL,`expire` BIGINT,primary key (`lock_key`) ) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0); INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0); INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0); INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
来源地址:seata库,
- 修改Seata的
seata/conf/application.yml
配置文件(注意:!!!这里最好先备份一下此配置文件,预防配置出错!!!)# Copyright 1999-2019 Seata.io Group. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. server:port: 7091 spring:application:name: seata-server logging:config: classpath:logback-spring.xmlfile:path: ${log.home:${user.home}/logs/seata}extend:logstash-appender:destination: 127.0.0.1:4560kafka-appender:bootstrap-servers: 127.0.0.1:9092topic: logback_to_logstash console:user:username: seatapassword: seata#------------新增的内容--------------- seata:config:type: nacosnacos: #nacos的配置server-addr: 127.0.0.1:8848 namespace:group: SEATA_GROUP #后续自己在nacos里面新建,不想新建SEATA_GROUP,就写DEFAULT_GROUPusername: nacospassword: nacosregistry:type: nacosnacos: #服务注册 application: seata-serverserver-addr: 127.0.0.1:8848group: SEATA_GROUP #后续自己在nacos里面新建,不想新建SEATA_GROUP,就写DEFAULT_GROUPnamespace:cluster: defaultusername: nacospassword: nacosstore:mode: dbdb:datasource: druiddb-type: mysqldriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=trueuser: root #写自己的数据库的用户名password: yourPassword #数据库密码min-conn: 10max-conn: 100#--------这几张表就是上面创建的Seata专属库中的表-----------global-table: global_table branch-table: branch_table lock-table: lock_table distributed-lock-table: distributed_lock #--------------------------------------------------query-limit: 1000max-wait: 5000#——--------------------# server:# service-port: 8091 #If not configured, the default is '${server.port} + 1000' security:secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017tokenValidityInMilliseconds: 1800000ignore:urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/metadata/v1/**
- 启动Seata:到
seata\bin\
目录下运行seata-server.bat
- 测试:访问默认网址http://localhost:7091/,账号密码默认:Seata
Seata案例实战
前置代码
- sql准备:
建seata order库+建t order表+undo log表CREATE DATABASE seata_order; USE seata_order; CREATE TABLE t_order( `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id', `product_id` BIGINT(11)DEFAULT NULL COMMENT '产品id', `count` INT(11) DEFAULT NULL COMMENT '数量', `money` DECIMAL(11,0) DEFAULT NULL COMMENT '金额', `status` INT(1) DEFAULT NULL COMMENT '订单状态: 0:创建中; 1:已完结' )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; SELECT * FROM t_order; -- 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 = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table'; ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`);
建seata storage库+建t storage 表+undo log表
CREATE DATABASE seata_storage; USE seata_storage; CREATE TABLE t_storage(`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,`product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',`total` INT(11) DEFAULT NULL COMMENT '总库存',`used` INT(11) DEFAULT NULL COMMENT '已用库存',`residue` INT(11) DEFAULT NULL COMMENT '剩余库存' )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; INSERT INTO t_storage(`id`,`product_id`,`total`,`used`,`residue`)VALUES('1','1','100','0','100'); SELECT * FROM t_storage; -- 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 = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table'; ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`);
自建seata account库+建t account 表+undo log表
create database seata_account; use seata_account; CREATE TABLE t_account(`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',`user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',`total` DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',`used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用余额',`residue` DECIMAL(10,0) DEFAULT '0' COMMENT '剩余可用额度' )ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; INSERT INTO t_account(`id`,`user_id`,`total`,`used`,`residue`)VALUES('1','1','1000','0','1000'); SELECT * FROM t_account; -- 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 = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table'; ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`);
- 添加两个Openfeign的接口:
账户对外暴漏的接口
@FeignClient(value = "seata-account-service") public interface SeataAccountFeignApi {//扣减账户余额@PostMapping("/account/decrease")ResultData decrease(@RequestParam("userId") Long userId, @RequestParam("money") Long money); }
库存对外暴漏的接口
@FeignClient(value = "seata-storage-service") public interface SeataStorageFeignApi { // 扣减库存@PostMapping(value = "/storage/decrease")ResultData decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count); }
- 订单服务的创建
pom依赖导入
<dependencies><!-- nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--alibaba-seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--loadbalancer--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!--cloud-api-commons--><dependency><groupId>com.chyb.cloud</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency><!--web + actuator--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!--SpringBoot集成druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId></dependency><!-- Swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId></dependency><!--mybatis和springboot整合--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!--MyBatisPlus:这里大家可以不引入我只是为了做一个MyBatisPlus的测试就导入此依赖了--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus</artifactId><version>3.5.11</version></dependency><!--Mysql数据库驱动8 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--persistence--><dependency><groupId>javax.persistence</groupId><artifactId>persistence-api</artifactId></dependency><!--通用Mapper4--><dependency><groupId>tk.mybatis</groupId><artifactId>mapper</artifactId></dependency><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><!-- fastjson2 --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version><scope>provided</scope></dependency><!--test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
yml配置文件启动类
server:port: 2001
spring:application:name: seata-order-servicecloud:nacos:discovery:server-addr: localhost:8848 #Nacos服务注册中心地址# ==========applicationName + druid-mysql8 driver===================datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata_order?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=trueusername: rootpassword: 123456
# ========================mybatis===================
mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.chyb.cloud.entitiesconfiguration:map-underscore-to-camel-case: true
# ========================seata===================
seata:registry:type: nacosnacos:server-addr: 127.0.0.1:8848namespace: ""group: SEATA_GROUPapplication: seata-servertx-service-group: default_tx_group # 事务组,由它获得TC服务的集群名称service:vgroup-mapping: # 点击源码分析default_tx_group: default # 事务组与TC服务集群的映射关系data-source-proxy-mode: AT
logging:level:io:seata: info
@SpringBootApplication
@EnableDiscoveryClient/*服务注册*/
@EnableFeignClients/*openfeign*/
@MapperScan("com.chyb.cloud.mapper")
public class SeataOrderMainApp2001 {public static void main(String[] args) {SpringApplication.run(SeataOrderMainApp2001.class, args);}}
业务代码:
@RestController
@RequestMapping("/order")
public class OrderController {@Resourceprivate OrderService orderService;@GetMapping("/create")public ResultData create(Order order){orderService.create(order);return ResultData.success("订单创建成功!");}
}
@Slf4j
@Service
public class OrderServiceImpl implements OrderService
{@Resourceprivate OrderMapper orderMapper;@Resource//订单微服务通过OpenFeign去调用库存微服务private SeataStorageFeignApi storageFeignApi;@Resource//订单微服务通过OpenFeign去调用账户微服务private SeataAccountFeignApi accountFeignApi;@Override
// @GlobalTransactional(name = "chyb-create-order",rollbackFor = Exception.class) //ATpublic void create(Order order) {//xid检查String xid = RootContext.getXID();//1. 新建订单log.info("==================>开始新建订单"+"\t"+"xid_order:" +xid);//订单状态status:0:创建中;1:已完结order.setStatus(0);int result = orderMapper.insertSelective(order);//插入订单成功后获得插入mysql的实体对象Order orderFromDB = null;if(result > 0){orderFromDB = orderMapper.selectOne(order);//orderFromDB = orderMapper.selectByPrimaryKey(order.getId());log.info("-------> 新建订单成功,orderFromDB info: "+orderFromDB);System.out.println();//2. 扣减库存log.info("-------> 订单微服务开始调用Storage库存,做扣减count");storageFeignApi.decrease(orderFromDB.getProductId(), orderFromDB.getCount());log.info("-------> 订单微服务结束调用Storage库存,做扣减完成");System.out.println();//3. 扣减账号余额log.info("-------> 订单微服务开始调用Account账号,做扣减money");accountFeignApi.decrease(orderFromDB.getUserId(), orderFromDB.getMoney());log.info("-------> 订单微服务结束调用Account账号,做扣减完成");System.out.println();//4. 修改订单状态//订单状态status:0:创建中;1:已完结log.info("-------> 修改订单状态");orderFromDB.setStatus(1);Example whereCondition=new Example(Order.class);Example.Criteria criteria=whereCondition.createCriteria();criteria.andEqualTo("userId",orderFromDB.getUserId());criteria.andEqualTo("status",0);int updateResult = orderMapper.updateByExampleSelective(orderFromDB, whereCondition);log.info("-------> 修改订单状态完成"+"\t"+updateResult);log.info("-------> orderFromDB info: "+orderFromDB);}System.out.println();log.info("==================>结束新建订单"+"\t"+"xid_order:" +xid);}
}
import com.chyb.cloud.entities.Order;
import tk.mybatis.mapper.common.Mapper;
public interface OrderMapper extends Mapper<Order> {
}
- 账户服务和库存服务
这两个就直接写业务逻辑,其他的如实体类大家就自己创建
/*库存*/
@Service
@Slf4j
public class StorageServiceImpl implements StorageService {@Autowiredprivate StorageMapper storageMapper;@Overridepublic void decrease(Long productId, Integer count) {log.info("------->storage-service中扣减库存开始");/*这里为了方便我直接使用了MyBatisPlus*/LambdaUpdateWrapper<Storage> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper/*条件*/.eq(Storage::getProductId, productId)/*字段自增*/.setIncrBy(Storage::getUsed, count)/*字段自减*/.setDecrBy(Storage::getResidue, count);int update = storageMapper.update(updateWrapper);log.info("------->storage-service中扣减库存结束");}
}
/*账户*/
@Service
@Slf4j
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountMapper accountMapper;@Overridepublic void decrease(Long userId, Long money) {log.info("------->account-service中扣减账户余额开始");LambdaUpdateWrapper<Account> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(Account::getUserId, userId).setIncrBy(Account::getUsed, money).setDecrBy(Account::getTotal, money);accountMapper.update(updateWrapper);
// myTimeOut();
// int a = 10 / 0;}/*** 模拟超时异常,全局事务回滚*/private static void myTimeOut() {try {TimeUnit.SECONDS.sleep(65);} catch (InterruptedException e) {e.printStackTrace();}}
}
运行测试是否可以运行若是可以便是若是是在不行可以直接使用gitee将代码克隆下来网址为:https://gitee.com/banhuayue/springCloud-Alibaba-code.git
对应运行的代码是:2001、2002、2003,其中cloud-api-commons是Openfeign对外暴漏的API接口的demo
添加全局注解 @GlobalTransactional
订单service中的方法上添加一个此注解
@GlobalTransactional
,接口
// 属性解释name是查看Seata后台的transactionName,rollbackFor为指定AT模式(默认是AT模式)@GlobalTransactional(name = "chyb-create-order",rollbackFor = Exception.class)
开启超时方法
myTimeOut
测试:
启动三个服务访问订单的Swagger测试接口order-controller这里就不展示效果了 (。・_・。)ノ懒~~~ ,大家可以在超时之前查看:三个服务的数据库、还有对应的UNDO_LOG表、Seata的后台全局信息和事务信息
上述大部分代码以上传到gitee:https://gitee.com/banhuayue/springCloud-Alibaba-code.git
笔记参考来自尚硅谷
相关文章:

springCloud/Alibaba常用中间件之Seata分布式事务
文章目录 SpringCloud Alibaba:依赖版本补充Seata处理分布式事务(AT模式)AT模式介绍核心组件介绍AT的工作流程:两阶段提交(**2PC**) Seata-AT模式使用Seata(2.0.0)下载、配置和启动Seata案例实战前置代码添加全局注解 GlobalTransactional Sp…...

Datawhale FastAPI Web框架5月第1次笔记
原课程地址: FastAPI Web框架https://www.datawhale.cn/learn/summary/164本次难点: 切换python的版本为3.10 作业过程 启动: jupyter notebook 首先我们要确保自己的python版本是3.10 import sys print(sys.version) 第一个fastapi…...

操作系统:os概述
操作系统:OS概述 程序、进程与线程无极二级目录三级目录 程序、进程与线程 指令执行需要那些条件?CPU内存 需要数据和 无极 二级目录 三级目录...

LLaMA-Factory:环境准备
一、硬件和系统 操作系统: Ubuntu 24.04.2 LTS(64位)GPU: NVIDIA RTX 4090 笔记本 GPU,16GB显存CPU: 建议高性能多核 CPU(如 Intel i7/i9 或 AMD Ryzen 7/9)以支持数据预处理,我的是32核。RAM: 至少 32GB&…...

ArrayList-集合使用
自动扩容,集合的长度可以变化,而数组长度不变,集合更加灵活。 集合只能存引用数据类型,不能直接存基本数据类型,除非包装 ArrayList会拿[]展示数据...

一分钟用 MCP 上线一个 贪吃蛇 小游戏(CodeBuddy版)
我正在参加CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 你好,我是悟空。 背景 上篇我们用 MCP 上线了一个 2048 小游戏,这次我们继续做一个 …...
pytorch小记(二十二):全面解读 PyTorch 的 `torch.cumprod`——累积乘积详解与实战示例
pytorch小记(二十二):全面解读 PyTorch 的 torch.cumprod——累积乘积详解与实战示例 一、函数签名与参数说明二、基础用法1. 一维张量累积乘积2. 二维张量按行/按列累积 三、dtype 参数:避免整数溢出与提升精度四、典…...

TTS:F5-TTS 带有 ConvNeXt V2 的扩散变换器
1,项目简介 F5-TTS 于英文生成领域表现卓越,发音标准程度在本次评测软件中独占鳌头。再者,官方预设的多角色生成模式独具匠心,能够配置多个角色,一次性为多角色、多情绪生成对话式语音,别出心裁。 最低配置…...
强化学习笔记(一)基本概念
文章目录 1. 强化学习 (Reinforcement Learning, RL) 概述1.1 与监督学习 (Supervised Learning, SL) 的对比监督学习的特点:强化学习的特点: 2. 核心概念与术语2.1 策略 (Policy, π)2.2 价值函数 (Value Function)2.3 模型 (Model)2.4 回报 (Return, G)2.5 其他重要术语 3. 标…...

大型语言模型中的QKV与多头注意力机制解析
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...

基于地图的数据可视化:解锁地理数据的真正价值
目录 一、基于地图的数据可视化概述 (一)定义与内涵 (二)重要性与意义 二、基于地图的数据可视化的实现方式 (一)数据收集与整理 (二)选择合适的可视化工具 (三&a…...
利用自适应双向对比重建网络与精细通道注意机制实现图像去雾化技术的PyTorch代码解析
利用自适应双向对比重建网络与精细通道注意机制实现图像去雾化技术的PyTorch代码解析 漫谈图像去雾化的挑战 在计算机视觉领域,图像复原一直是研究热点。其中,图像去雾化技术尤其具有实际应用价值。然而,复杂的气象条件和多种因素干扰使得这…...

分布式链路跟踪
目录 链路追踪简介 基本概念 基于代理(Agent)的链路跟踪 基于 SDK 的链路跟踪 基于日志的链路跟踪 SkyWalking Sleuth ZipKin 链路追踪简介 分布式链路追踪是一种监控和分析分布式系统中请求流动的方法。它能够记录和分析一个请求在系统中经历的每…...

刷leetcodehot100返航版--二叉树
二叉树理论基础 二叉树的种类 满二叉树和完全二叉树,二叉树搜索树 满二叉树 如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。 节点个数2^n-1【n为树的深度】 完全二叉树 在完全二叉树…...
chmod 777含义:
1.chmod 777 的含义及其在文件权限中的作用 chmod 777 是一种用于修改 Unix 和 Linux 系统中文件或目录权限的命令。它赋予指定文件或目录的所有用户(文件所有者、所属组成员以及其他用户)完全的访问权限,即 读取 (Read)、写入 (Write) 和 执…...
AGI大模型(21):混合检索之混合搜索
为了执行混合搜索,我们结合了 BM25 和密集检索的结果。每种方法的分数均经过标准化和加权以获得最佳总体结果 1 代码 先编写 BM25搜索的代码,再编写密集检索的代码,最后进行混合。 from rank_bm25 import BM25Okapi from nltk.tokenize import word_tokenize import jieb…...

双重差分模型学习笔记4(理论)
【DID最全总结】90分钟带你速通双重差分!_哔哩哔哩_bilibili 目录 总结:双重差分法(DID)在社会科学中的应用:理论、发展与前沿分析 一、DID的基本原理与核心思想 二、经典DID:标准模型与应用案例 三、…...

Mysql 8.0.32 union all 创建视图后中文模糊查询失效
记录问题,最近在使用union all聚合了三张表的数据,创建视图作为查询主表,发现字段值为中文的筛选无法生效.......... sql示例: CREATE OR REPLACE VIEW test_view AS SELECTid,name,location_address AS address,type,"1" AS data_type,COALESCE ( update_time, cr…...

Jenkins 执行器(Executor)如何调整限制?
目录 现象原因解决 现象 Jenkins 构建时,提示如下: 此刻的心情正如上图中的小老头,火冒三丈,但是不要急,因为每一次错误,都是系统中某个环节在说‘我撑不住了’。 原因 其实是上图的提示表示 Jenkins 当…...
Android 中 权限分类及申请方式
在 Android 中,权限被分为几个不同的类别,每个类别有不同的申请和管理方式。 一、 普通权限(Normal Permissions) 普通权限通常不会对用户隐私或设备安全造成太大风险。这些权限在应用安装时自动授予,无需用户在运行时手动授权。 android.permission.INTERNETandroid.pe…...

编程错题集系列(一)
编程错题集系列(一) 人生海海,山山而川。 谨以此系列作为自己一路的见证。本期重点:明明已经安装相关库,但在PyCharm中无法调用 最大的概率是未配置合适的解释器,也就是你的书放在B房间,你在A…...

【原创】基于视觉大模型gemma-3-4b实现短视频自动识别内容并生成解说文案
📦 一、整体功能定位 这是一个用于从原始视频自动生成短视频解说内容的自动化工具,包含: 视频抽帧(可基于画面变化提取关键帧) 多模态图像识别(每帧图片理解) 文案生成(大模型生成…...

Spark(32)SparkSQL操作Mysql
(一)准备mysql环境 我们计划在hadoop001这台设备上安装mysql服务器,(当然也可以重新使用一台全新的虚拟机)。 以下是具体步骤: 使用finalshell连接hadoop001.查看是否已安装MySQL。命令是: rpm -qa|grep ma…...

基于 Python 的界面程序复现:标准干涉槽型设计计算及仿真
基于 Python 的界面程序复现:标准干涉槽型设计计算及仿真 在工业设计与制造领域,刀具的设计与优化是提高生产效率和产品质量的关键环节之一。本文将介绍如何使用 Python 复现一个用于标准干涉槽型设计计算及仿真的界面程序,旨在帮助工程师和…...

c++成员函数返回类对象引用和直接返回类对象的区别
c成员函数返回类对象引用和直接返回类对象的区别 成员函数直接返回类对象(返回临时对象,对象拷贝) #include <iostream> class MyInt { public:int value;//构造函数explicit MyInt(int v0) : value(v){}//加法操作,返回对象副本&…...
AGI大模型(20):混合检索之rank_bm25库来实现词法搜索
1 混合检索简介 混合搜索结合了两种检索信息的方法 词法搜索 (BM25) :这种传统方法根据精确的关键字匹配来检索文档。例如,如果您搜索“cat on the mat”,它将找到包含这些确切单词的文档。 基于嵌入的搜索(密集检索) :这种较新的方法通过比较文档的语义来检索文档。查…...

数字化转型- 数字化转型路线和推进
数字化转型三个阶段 百度百科给出的企业的数字化转型包括信息化、数字化、数智化三个阶段 信息化是将企业在生产经营过程中产生的业务信息进行记录、储存和管理,通过电子终端呈现,便于信息的传播与沟通。数字化通过打通各个系统的互联互通,…...
字体样式集合
根据您提供的字体样式列表,以下是分类整理后的完整字体样式名称(不含数量统计): 基础样式 • Regular • Normal • Plain • Medium • Bold • Black • Light • Thin • Heavy • Ultra • Extra • Semi • Hai…...

IP68防水Type-C连接器实测:水下1米浸泡72小时的生存挑战
IP68防水Type-C连接器正成为户外设备、水下仪器和高端消费电子的核心组件。其宣称的“1米水深防护”是否真能抵御长时间浸泡?我们通过极限实测,将三款主流品牌IP68防水Type-C连接器沉入1米盐水(模拟海水浓度)中持续72小时…...

【技术追踪】InverseSR:使用潜在扩散模型进行三维脑部 MRI 超分辨率重建(MICCAI-2023)
LDM 实现三维超分辨率~ 论文:InverseSR: 3D Brain MRI Super-Resolution Using a Latent Diffusion Model 代码:https://github.com/BioMedAI-UCSC/InverseSR 0、摘要 从研究级医疗机构获得的高分辨率(HR)MRI 扫描能够提供关于成像…...