springcloud第三讲
创始人
2025-05-31 07:16:50
0

目录

三、springcloud03

3.1 普通事务

3.2 演示事务

3.2.1、建库

3.2.2 创建父工程

3.2.3 编写公共微服务

3.2.4 编写账户微服务

3.2.5 编写库存微服务

3.2.6 编写订单微服务

3.2.7 测试

3.3 分布式事务

3.4 介绍seata

3.5 搭建seata服务器

3.5.1 解决集群共享数据

3.5.2 seata连接nacos

3.6 配置微服务客户端

3.6.1 创建数据库

3.6.2 修改微服务代码

三、springcloud03

3.1 普通事务

普通事务

1、ACID事务的特点:原子性、一致性、隔离性、持久性

2、事务并发带来的问题:脏读、幻读、不可重复读、丢失修改

3、我们可以使用事务的隔离级别来解决

读未提交、读已提交、可重复读、串行化

事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列SQL操作,这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行。

 

3.2 演示事务

3.2.1、建库

创建三个数据库,每个数据库一张表,表名和库名一致即可,这里使用的是mysql5.7的建表语句,没有collect

/*Navicat Premium Data Transfer
​Source Server         : localhost_3306Source Server Type    : MySQLSource Server Version : 80011Source Host           : localhost:3306Source Schema         : seata_account
​Target Server Type    : MySQLTarget Server Version : 80011File Encoding         : 65001
​Date: 28/11/2022 15:28:32
*/
​
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
​
-- ----------------------------
-- Table structure for t_account
-- ----------------------------
DROP TABLE IF EXISTS `t_account`;
CREATE TABLE `t_account`  (`id` int(11) NOT NULL AUTO_INCREMENT,`user_id` int(11) NULL DEFAULT NULL,`total` int(11) NULL DEFAULT NULL,`used` int(11) NULL DEFAULT NULL,`residue` int(11) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4;
​
-- ----------------------------
-- Records of t_account
-- ----------------------------
INSERT INTO `t_account` VALUES (1, 1, 1000, 100, 900);
​
SET FOREIGN_KEY_CHECKS = 1;
/*Navicat Premium Data Transfer
​Source Server         : localhost_3306Source Server Type    : MySQLSource Server Version : 80011Source Host           : localhost:3306Source Schema         : seata_order
​Target Server Type    : MySQLTarget Server Version : 80011File Encoding         : 65001
​Date: 28/11/2022 15:28:38
*/
​
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
​
-- ----------------------------
-- Table structure for t_order
-- ----------------------------
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order`  (`id` int(11) NOT NULL AUTO_INCREMENT,`user_id` int(11) NULL DEFAULT NULL,`product_id` int(11) NULL DEFAULT NULL,`count` int(11) NULL DEFAULT NULL,`money` bigint(20) NULL DEFAULT NULL,`status` int(11) NULL DEFAULT 0 COMMENT '0 未支付  1 已支付',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 46 CHARACTER SET = utf8mb4;
​
-- ----------------------------
-- Records of t_order
-- ----------------------------
INSERT INTO `t_order` VALUES (47, 1, 1, 5, 100, 1);
​
SET FOREIGN_KEY_CHECKS = 1;
/*Navicat Premium Data Transfer
​Source Server         : localhost_3306Source Server Type    : MySQLSource Server Version : 80011Source Host           : localhost:3306Source Schema         : seata_storage
​Target Server Type    : MySQLTarget Server Version : 80011File Encoding         : 65001
​Date: 28/11/2022 15:28:45
*/
​
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
​
-- ----------------------------
-- Table structure for t_storage
-- ----------------------------
DROP TABLE IF EXISTS `t_storage`;
CREATE TABLE `t_storage`  (`id` int(11) NOT NULL AUTO_INCREMENT,`product_id` int(11) NULL DEFAULT NULL,`total` int(11) NULL DEFAULT NULL COMMENT '总量',`used` int(11) NULL DEFAULT NULL COMMENT '已经使用的数量',`residue` int(11) NULL DEFAULT NULL COMMENT '剩余的数据量',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8;
​
-- ----------------------------
-- Records of t_storage
-- ----------------------------
INSERT INTO `t_storage` VALUES (1, 1, 100, 0, 100);
​
SET FOREIGN_KEY_CHECKS = 1;

3.2.2 创建父工程

删掉src,只保留pom文件即可


4.0.0com.ykqseata_parent1.0-SNAPSHOTseata-commonseata-accountseata-orderseata-storagepomorg.springframework.bootspring-boot-starter-parent2.3.12.RELEASE1.8UTF-8UTF-8Hoxton.SR82.2.3.RELEASEorg.springframework.cloudspring-cloud-dependencies${spring-cloud.version}pomimportcom.alibaba.cloudspring-cloud-alibaba-dependencies${spring-cloud-alibaba.version}pomimport

3.2.3 编写公共微服务

1、pom文件


seata_parentcom.ykq1.0-SNAPSHOT4.0.0seata-commonorg.projectlomboklombok

2、实体类

package com.ykq.entity;
​
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//账户
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {private int id;private int userId;private int total;private int used;private int residue;
}
package com.ykq.entity;
​
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
//统一返回结果
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult {
​private Integer code;private String message;private T data;public CommonResult(Integer code, String message) {this(code, message, null);}
}
package com.ykq.entity;
​
import lombok.Data;
//订单
@Data
public class Order {private int id;private int userId;private int productId;private double money;private int count;private int status;
}
package com.ykq.entity;
​
import lombok.Data;
//库存
@Data
public class Storage {private int id;private int productId;private int total;private int used;//被用了多少个private int residue;//剩余的个数。
}

3.2.4 编写账户微服务

1、pom文件


seata_parentcom.ykq1.0-SNAPSHOT4.0.0seata-accountcom.ykqseata-common1.0-SNAPSHOTcom.alibaba.cloudspring-cloud-starter-alibaba-nacos-discoverycom.alibaba.cloudspring-cloud-starter-alibaba-nacos-configorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-jdbcorg.mybatis.spring.bootmybatis-spring-boot-starter2.1.3org.springframework.cloudspring-cloud-starter-openfeignorg.springframework.bootspring-boot-devtoolsruntimetruemysqlmysql-connector-javaruntimeorg.springframework.bootspring-boot-configuration-processortrueorg.projectlomboklomboktrueorg.springframework.bootspring-boot-starter-testtestorg.junit.vintagejunit-vintage-enginemysqlmysql-connector-java

2、配置文件

server:port: 8002
spring:# 数据源配置datasource:url: jdbc:mysql://localhost:3306/seata_account?serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 密码#配置服务名application:name: seata-account#nacos注册中心的地址cloud:nacos:discovery:server-addr: localhost:8848
mybatis:mapperLocations: classpath:mapper/*.xml
logging:level:com.ykq.dao: debug

3、mapper文件



update t_account set used = used + #{money},residue = residue - #{money}where user_id=#{userId}

4、mapper接口

package com.ykq.dao;
​
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
​
import java.math.BigDecimal;
​
@Mapper
public interface AccountDao {
​void decrease(@Param("userId") int userId, @Param("money") double money);
}

5、service接口

package com.ykq.service;
​
public interface AccountService {
​void decrease(int userId, double money);
}

6、service实现层

package com.ykq.service.impl;
​
import com.ykq.dao.AccountDao;
import com.ykq.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
​
@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Overridepublic void decrease(int userId, double money) {accountDao.decrease(userId,money);}
}

7、controller层

package com.ykq.controller;
​
import com.ykq.entity.CommonResult;
import com.ykq.service.AccountService;
import org.springframework.web.bind.annotation.*;
​
import javax.annotation.Resource;
import java.math.BigDecimal;
​
@RestController
@RequestMapping(value = "account")
public class AccountController {
​@Resourceprivate AccountService accountService;
​/*** 根据用户id扣余额* @param userId* @param money* @return*/@PostMapping("/increase/{userId}/{money}")public CommonResult increase(@PathVariable("userId")Integer userId,@PathVariable("money")double money) {accountService.decrease(userId, money);return new CommonResult(200,"账户余额扣减成功,哈哈哈");}
}

8、启动类

package com.ykq;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
​
//1.3
@SpringBootApplication
@EnableDiscoveryClient
public class AccountApplication {
​public static void main(String[] args) {SpringApplication.run(AccountApplication.class, args);}
​
}

3.2.5 编写库存微服务

1、pom文件


seata_parentcom.ykq1.0-SNAPSHOT4.0.0seata-storagecom.ykqseata-common1.0-SNAPSHOTcom.alibaba.cloudspring-cloud-starter-alibaba-nacos-discoverycom.alibaba.cloudspring-cloud-starter-alibaba-nacos-configorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-jdbcorg.mybatis.spring.bootmybatis-spring-boot-starter2.1.3org.springframework.cloudspring-cloud-starter-openfeignorg.springframework.bootspring-boot-devtoolsruntimetruemysqlmysql-connector-javaruntimeorg.springframework.bootspring-boot-configuration-processortrueorg.projectlomboklomboktrueorg.springframework.bootspring-boot-starter-testtestorg.junit.vintagejunit-vintage-engine

和账户微服务的pom文件差不多,可以参考账户微服务

2、配置文件

server:port: 8003
spring:# 数据源配置datasource:url: jdbc:mysql://localhost:3306/seata_storage?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 密码application:name: seata-storagecloud:nacos:discovery:server-addr: localhost:8848alibaba:seata:tx-service-group: fsp_tx_group
mybatis:mapperLocations: classpath:mapper/*.xml
logging:level:com.ykq.dao: debug

3、mapper文件



update t_storage set used = used + #{count},residue = residue -#{count}where product_id = #{productId}

4、mapper接口

package com.ykq.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
​
public interface StorageDao {void decrease(@Param("productId") int productId,@Param("count") Integer count);
}
​
​

5、service接口

package com.ykq.service;
​
public interface StorageService {void decrease(int productId, Integer count);
}

6、service实现类

package com.ykq.service.impl;
​
import com.ykq.dao.StorageDao;
import com.ykq.service.StorageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
​
@Service
@Slf4j
public class StorageServiceImpl implements StorageService {@Autowiredprivate StorageDao storageDao;@Override@Transactionalpublic void decrease(int productId, Integer count) {log.info("库存扣减开始----");storageDao.decrease(productId,count);log.info("库存扣减结束----");}
}

7、controller层

package com.ykq.controller;
​
​
import com.ykq.entity.CommonResult;
import com.ykq.service.StorageService;
import org.springframework.web.bind.annotation.*;
​
import javax.annotation.Resource;
​
@RestController
@RequestMapping(value = "/storage")
public class StorageController {
​@Resourceprivate StorageService storageService;
​/*** 根据商品id扣减库存* @param productId* @param count* @return*/@PostMapping("/increase/{productId}/{count}")CommonResult increase(@PathVariable("productId") int productId, @PathVariable("count") int count){storageService.decrease(productId, count);return new CommonResult(200,"库存扣减成功,哈哈哈哈");}
}

8、启动类

package com.ykq;
​
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.transaction.annotation.EnableTransactionManagement;
​
@SpringBootApplication
@MapperScan("com.ykq.dao")
public class StorageApplication {
​public static void main(String[] args) {SpringApplication.run(StorageApplication.class, args);}
​
}
​

3.2.6 编写订单微服务

1、pom文件


seata_parentcom.ykq1.0-SNAPSHOT4.0.0seata-ordercom.ykqseata-common1.0-SNAPSHOT
​
​com.alibaba.cloudspring-cloud-starter-alibaba-nacos-discoverycom.alibaba.cloudspring-cloud-starter-alibaba-nacos-configorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-jdbcorg.mybatis.spring.bootmybatis-spring-boot-starter2.1.3org.springframework.cloudspring-cloud-starter-openfeignorg.springframework.bootspring-boot-devtoolsruntimetruemysqlmysql-connector-javaruntimeorg.springframework.bootspring-boot-configuration-processortrueorg.springframework.bootspring-boot-starter-testtestorg.junit.vintagejunit-vintage-engine

2、配置文件

server:port: 8001
​
spring:application:name: seata-order
# 数据源datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata_order?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8username: rootpassword: 密码
# 把该服务注册到nacos上cloud:nacos:discovery:server-addr: localhost:8848
​
# 配置mybatis映射文件的路径
mybatis:mapperLocations: classpath:mapper/*.xml
# 显示mybatis执行的sql语句
logging:level:com.ykq.dao: debug

3、mapper文件




​
​insert into t_order(user_id,product_id,count,money,status)value (#{userId},#{productId},#{count},#{money},0)update t_order set status = 1where id = #{id} and status = 0

4、mapper接口

package com.ykq.dao;
​
import com.ykq.entity.Order;
​
public interface OrderDao {void saveOrder(Order order);
​void updateStatus(int id);
}

5、feign接口

因为要实现远程调用,所以这里要编写feign接口

package com.ykq.feign;
​
import com.ykq.entity.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
​
@FeignClient(value="seata-account")
public interface AccountFeign {
​@PostMapping("/account/increase/{userId}/{money}")public CommonResult increase(@PathVariable("userId")Integer userid, @PathVariable("money")double money);
}
package com.ykq.feign;
​
import com.ykq.entity.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
​
@FeignClient("seata-storage")
public interface StorageFeign {@PostMapping("/storage/increase/{productId}/{count}")CommonResult increase(@PathVariable("productId") int productId, @PathVariable("count") int count);
}

6、service接口

package com.ykq.service;
​
import com.ykq.entity.Order;
​
public interface OrderService {void saveOrder(Order order);
}
​

7、service实现类

package com.ykq.service.impl;
​
import com.ykq.dao.OrderDao;
import com.ykq.entity.Order;
import com.ykq.feign.AccountFeign;
import com.ykq.feign.StorageFeign;
import com.ykq.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
​
import javax.annotation.Resource;
​
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
​@Autowiredprivate OrderDao orderDao;
​@Autowiredprivate AccountFeign accountFeign;//表示调用的远程服务。
​@Autowiredprivate StorageFeign storageFeign;
​@Override@Transactionalpublic void saveOrder(Order order) {log.info("-------->开始创建新订单");orderDao.saveOrder(order);
​log.info("-------订单微服务开始调用账户,做扣减");accountFeign.increase(order.getUserId(),order.getMoney()); //当前的事务回滚只会回滚自己的数据log.info("-------订单微服务开始调用账户,做扣减end");log.info("--------订单微服务开始调用库存,做扣减");storageFeign.increase(order.getProductId(),order.getCount());log.info("-------订单微服务开始调用库存,做扣减end");
​
​log.info("-------修改订单状态");orderDao.updateStatus(order.getId());
​log.info("-------修改订单状态结束");
​log.info("--------下订单结束了,哈哈哈哈");}
​
}

8、controller层

package com.ykq.controller;
​
import com.ykq.entity.CommonResult;
import com.ykq.entity.Order;
import com.ykq.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
@RequestMapping("order")
public class OrderController {@Autowiredprivate OrderService orderService;@GetMapping("addOrder")public CommonResult addOrder(Order order){try {orderService.saveOrder(order);return new CommonResult(2000, "下单成功");}catch (Exception e){return new CommonResult(5000, "下单失败");}}
}

9、启动类

package com.ykq;
​
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.transaction.annotation.EnableTransactionManagement;
​
@SpringBootApplication //排除原有的数据源的配置
@MapperScan("com.ykq.dao")
@EnableFeignClients //开启feign的注解
@EnableTransactionManagement
public class OrderApp {public static void main(String[] args) {SpringApplication.run(OrderApp.class,args);}
}

3.2.7 测试

代码编写完成,如果是粘贴的整个项目,配置文件红maven仓库也需要修改为自己的

3.2.7.1 成功测试

1、数据库初始状态

 

 

 

2、浏览器访问订单资源进行测试

请求为:http://localhost:8001/order/addOrder/?userId=1&productId=1&count=5&money=100

响应结果为:{"code":2000,"message":"下单成功","data":null}

 

3、响应成功后数据库

 

 

 

3.2.7.2 异常测试

人为的给订单微服务程序添加一个异常情况 【算术异常即可】比如:10/0

1、修改订单的service实现类

package com.ykq.service.impl;
​
import com.ykq.dao.OrderDao;
import com.ykq.entity.Order;
import com.ykq.feign.AccountFeign;
import com.ykq.feign.StorageFeign;
import com.ykq.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
​
import javax.annotation.Resource;
​
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
​@Autowiredprivate OrderDao orderDao;
​@Autowiredprivate AccountFeign accountFeign;//表示调用的远程服务。
​@Autowiredprivate StorageFeign storageFeign;
​@Override@Transactionalpublic void saveOrder(Order order) {log.info("-------->开始创建新订单");orderDao.saveOrder(order);
​log.info("-------订单微服务开始调用账户,做扣减");accountFeign.increase(order.getUserId(),order.getMoney()); //当前的事务回滚只会回滚自己的数据log.info("-------订单微服务开始调用账户,做扣减end");//添加异常int  c=10/0;log.info("--------订单微服务开始调用库存,做扣减");storageFeign.increase(order.getProductId(),order.getCount());log.info("-------订单微服务开始调用库存,做扣减end");
​
​log.info("-------修改订单状态");orderDao.updateStatus(order.getId());
​log.info("-------修改订单状态结束");
​log.info("--------下订单结束了,哈哈哈哈");}
}

注:这里异常添加在账户微服务之后,订单微服务之前

2、再次进行测试

请求:http://localhost:8001/order/addOrder/?userId=1&productId=1&count=5&money=100

请求结果:{"code":5000,"message":"下单失败","data":null}

3、查看数据库状态

 

 

 

3.3 分布式事务

分布式服务结构图如下:

 

因为每个事务都连接了自己的数据库,所以在进行事务操作时,

一个微服务(A)的成功,就会改变自己的数据库,即使其它微服务(B)失败,该微服务(A)也不会回滚自己的数据库

1. 借助rabbitMQ消息中间件
2. 可以自己编写代码完成分布式事务。
3. 借助于第三方分布式事务服务器。---阿里巴巴seata

3.4 介绍seata

seata官网地址:Seata部署指南

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT[两次提交]、TCC、SAGA 和XA 事务模式,为用户打造一站式的分布式解决方案。

这次主要练习的就是AT事务模式

 

Seata的执行流程如下:

1、A服务【订单微服务】的TM[事务发起者]向TC[seata服务端]申请开启一个全局事务,TC就会创建一个全局事务并返回一个唯一的XID

2、A服务开始远程调用B服务【账户微服务】,此时XID会在微服务的调用链上传播

3、B服务的RM向TC注册分支事务,并将其纳入XID对应的全局事务的管辖

4、B服务执行分支事务,向数据库做操作

5、全局事务调用链处理完毕,TM根据有无异常向TC发起全局事务的提交或者回滚

6、TC协调其管辖之下的所有分支事务, 决定是否回滚

根据上面的测试进行举例来看,每一部分的角色如下

TM:事务的发起者--下单

RM: 连接数据库--微服务

TC:全局事务管理者--seata服务器

XID:全局事务id

3.5 搭建seata服务器

seata官网:Release v1.3.0 · seata/seata · GitHub

seata在1.3.0之后支持集群模式

解压文件

 

3.5.1 解决集群共享数据

1、修改conf/file.conf文件,更改数据保存的位置

 

1.1将源码压缩包解压后,找到其中script文件夹,将其复制到seata文件目录下

 

1.2 找到script\server\db目录下的mysql.sql文件,就是我们需要的三张表,将表添加到mysql中的seata数据库

 

1.3 因为在设置数据库时,我们选择的驱动是8的驱动,所以我们也需要有8的jar包,默认读取的是lib文件目录下的jar包,所以可以将seata/lib/jdbc/目录下的jar包粘贴到seata/lib目录下

3.5.2 seata连接nacos

当seata是一个集群时,微服务连接seata会无法选择seata,将seata接入到nacos,别的微服务就可以在nacos中拉取对应的seata服务

1、找到seata/conf/registry.conf文件进行修改

 

2、上传配置文件到nacos配置中心

找到seata\script\config-center目录下的config.txt文件,修改其中的配置,修改完之后,这些配置是需要放到配置中心的

 

注!!!!!!!!!!!!!!

url那一行,在最后多了一个双引号,一定要删掉

然后使用脚本将这些修改好的配置文件,传到nacos注册中心,脚本位置在seata\script\config-center\nacos\nacos-config.sh,如果安装了git,那么这里双击此脚本,会自动调用git的控制台,执行命令,将配置上传到配置中心

 

再执行时,也可以指定nacos的信息

 

3、启动搭建好的seata服务器,他会自动注册到nacos中

找到seata\bin目录下的seata-server.bat文件,双击即可执行,

如果需要指定端口号,可以进入到cmd中使用命令执行

seata-server.bat -Dserver.port=8989

 

3.6 配置微服务客户端

3.6.1 创建数据库

在每一个微服务的数据库中建一张日志表,表结构在seata\script\client\at\db目录下的mysql.sql中

 

3.6.2 修改微服务代码

这里先使用storage模块进行测试

1、添加依赖

        com.alibaba.cloudspring-cloud-starter-alibaba-seataio.seataseata-spring-boot-starterio.seataseata-spring-boot-starter1.3.0

当然这里版本是对应的,所以不需要修改版本

        com.alibaba.cloudspring-cloud-starter-alibaba-seata

2、编写配置文件这里改为properties文件

server.port=8003
#配置数据源
spring.datasource.url=jdbc:mysql://localhost:3306/seata_storage?serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=密码
#
spring.application.name=seata-storage
#注册中心地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
#seata服务组的名字
spring.cloud.alibaba.seata.tx-service-group=guangzhou
seata.service.vgroup-mapping.guangzhou=default
#扫描mapper文件
mybatis.mapper-locations=classpath:mapper/*.xml
#日志扫描
logging.level.com.ykq.dao=debug
#seata注册类型
seata.registry.type=nacos
#注册中心的信息-----------------------------
#指定seata服务器所在的注册中心地址
seata.registry.nacos.server-addr=localhost:8848
#nacos的用户名和密码
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos
#指定seata服务器在注册中心的组名,默认为SEATA_GROUP
seata.registry.nacos.group=SEATA_GROUP
#指定seata服务器在注册中心的服务名,默认为seata-server
seata.registry.nacos.application=seata-server
#注册中心的信息----------------------------------
#指定seata服务器所在的配置中心地址
seata.config.nacos.server-addr=localhost:8848
#nacos的用户名和密码
seata.config.nacos.username=nacos
seata.config.nacos.password=nacos
#指定seata服务器在配置中心的组名,默认为SEATA_GROUP
seata.config.nacos.group=SEATA_GROUP

3、运行项目进行测试

报错: no available service 'default' found, please make sure registry config correct

在配置文件中添加一行,这里的guangzhou就是自己命名的seata组名

seata.service.vgroup-mapping.guangzhou=default

再次运行就可以成功了,就代表着客户端也搭建完毕,将其他模块修改一下即可

4、修改发起者的代码 [订单微服务的service实现类]

package com.ykq.service.impl;
​
import com.ykq.dao.OrderDao;
import com.ykq.entity.Order;
import com.ykq.feign.AccountFeign;
import com.ykq.feign.StorageFeign;
import com.ykq.service.OrderService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
​
import javax.annotation.Resource;
​
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
​@Autowiredprivate OrderDao orderDao;
​@Autowiredprivate AccountFeign accountFeign;//表示调用的远程服务。
​@Autowiredprivate StorageFeign storageFeign;
​@Override@GlobalTransactional   //使用这个注解进行分布式事务public void saveOrder(Order order) {log.info("-------->开始创建新订单");orderDao.saveOrder(order);
​log.info("-------订单微服务开始调用账户,做扣减");accountFeign.increase(order.getUserId(),order.getMoney()); //当前的事务回滚只会回滚自己的数据log.info("-------订单微服务开始调用账户,做扣减end");//添加异常// int  c=10/0;log.info("--------订单微服务开始调用库存,做扣减");storageFeign.increase(order.getProductId(),order.getCount());log.info("-------订单微服务开始调用库存,做扣减end");
​log.info("-------修改订单状态");orderDao.updateStatus(order.getId());
​log.info("-------修改订单状态结束");
​log.info("--------下订单结束了,哈哈哈哈");}
​
}

然后发起请求[数据库已经改为最初状态 拥有1000元,库存为100,订单数为0]

1、先验证正常情况

请求:http://localhost:8001/order/addOrder/?userId=1&productId=1&count=5&money=100

请求结果:{"code":2000,"message":"下单成功","data":null}

 

 

 

2、再测试异常情况

请求:http://localhost:8001/order/addOrder/?userId=1&productId=1&count=5&money=100

请求结果:

{"code":5000,"message":"下单失败","data":null}

 

 

其中一个微服务的事务失败,所有的事务都会回滚,

事务过程中,生成的信息存在日志表中,事务的信息存在seata数据库的三张表中,通过打断点可以看到中间的信息

相关内容

热门资讯

美国电动汽车“缺血”,根源居然... 美国商务部7月17日宣布,对来自中国的活性阳极材料(Active Anode Material,简称...
送门票发福利 银行助力提振消费... 随着促消费政策陆续出台,在前期探索后,银行支持提振和扩大消费的措施越来越具体、优惠越来越亲民,力度也...
黄金市场高位震荡:多空博弈加剧... 近期,黄金市场呈现 “量价齐升” 与 “资金分歧” 并存的复杂格局。截至 2025 年 7 月 23...
中科酷原完成数千万元战略融资,... 图片系AI生成 7月23日消息,中科酷原科技(武汉)有限公司(以下简称“中科酷原”)宣布完成数千万元...
前“二股东”因开赌场被捕,恒坤... 作者:何光 编辑:马克 科创板又迎来一家即将上会的“硬科技”企业。 厦门恒坤新材料科技股份有限公司(...
中国太平总经理李可东兼任太平财... 运营商财经网 实习生付桢/文 近日,太平养老官网发布的两则公告,经国家金融监督管理总局核准,李可东正...
紫燕百味鸡携手淘宝闪购,全域新... 近日,紫燕百味鸡与淘宝闪购平台携手联名点亮上海多个地标,通过淘宝闪购平台限时特惠活动,精准触达年轻消...
为何苹果在AI上进展缓慢?外媒... 【环球网科技综合报道】7月22日消息,据外媒AINEWS报道称,在人工智能浪潮汹涌澎湃的当下,苹果却...
原创 比... 原创首发 | 金角财经(ID: F-Jinjiao) 作者 | 温颖颖 又一“老登经济”泡沫破了。 ...
《低空经济基础设施框架指引(2... 7月23日,在2025国际低空经济博览会暨低空经济基础设施发展大会上,中国民用机场协会副秘书长黄伟宏...
坚定信心 勇挑大梁·产业新亮点... 来源:石家庄广播电视台 创新抢先一拍,成就钢铁独角兽 ——探访首钢智新的转型路径 7月16日,首钢...
奥克斯赴港上市前分红近38亿,... 奥克斯电气有限公司(下称“奥克斯”)离港交所上市又进一步。 近日,中国证监会官网显示,奥克斯取得境外...
外卖新战场:对决供应链 21世纪经济报道记者孔海丽、实习生吴佳芸、欧思岐 北京报道 外卖大战,正从手机屏幕里的补贴大战,烧向...
披着“稳定币”外衣!五大特征教... 本文转自【人民网】; 稳定币概念自6月底走热以来,一直是市场关注的焦点。在火热的市场情绪下,一些不法...
AI赋能,数智升级——浪潮智慧... 日前,中国卫生信息技术/健康医疗大数据应用交流大会暨软硬件与健康医疗产品展览会(2025CHITEC...
股票杠杆新玩法:ETF期权替代... 在股票投资的工具中,股票杠杆是一个需要谨慎对待的存在,它既可能为投资者带来放大的操作空间,也潜藏着相...
突破34万亿大关 公募基金管理... 公募基金管理规模再攀新高峰。天相投顾数据显示,截至2025年二季度末,公募基金管理规模突破34万亿元...
“巴菲特最爱”的中证红利质量E... 7月23日,两市午后走弱,截至14时49分,贵州茅台6连升,中证红利质量ETF(159209)跌0....
海南全岛封关时间定了!啥是封关... 国家发展改革委副主任王昌林7月23日在国新办发布会上表示,关于海南自贸港封关的具体时间,经党中央批准...