SpringCloudAlibaba国产之光

第一章 介绍

1.Spring Cloud Alibaba介绍

Spring Cloud Alibaba 是 Spring Cloud 体系下的一个子项目,专为中国市场打造,提供了对阿里巴巴的开源项目和云服务的支持。

2.主要组件

Nacos:作为一个动态服务发现、配置管理和服务管理平台

Sentinel:一个流量防护组件,主要用于服务的流量控制、熔断降级和系统负载保护。

RocketMQ:一种分布式消息中间件,具有高吞吐量、低延迟的特点,适用于大规模的消息系统。

Seata:一个分布式事务解决方案,支持高效并且易于使用的分布式事务处理,可以保证跨多个服务的事务一致性。

Alibaba Cloud OSS:对象存储服务(OSS)可以用于存储和管理大规模的非结构化数据,如图片、视频、日志文件等。

Alibaba Cloud SMS:短消息服务(SMS)提供了高效、可靠的短信发送能力,支持国内外短信发送。



第二章 Nacos服务注册与发现

1.官网地址

https://github.com/alibaba/nacos/releases/tag/2.3.0

启动

startup.cmd -m standalone

2.服务注册、发现

添加依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
配置yml
server:
  port: 9001
spring:
  application:
    name: nacos-pay-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # nacos地址

3.负载均衡

server:
  port: 9001
spring:
  application:
    name: nacos-pay-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # nacos地址

【相同的 name: nacos-pay-provider Nacos会进行自动的负载均衡】

4.配置中心

添加依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
配置中心命名规则

[应用名]-[环境名].[文件扩展名]

编写bootstrap.yml
spring:
  application:
    name: nacos-config #服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务发现注册地址
      config:
        server-addr: localhost:8848 #配置中心地址
        file-extension: yaml #用于配置中心后缀【这里一定要和配置中心中的后缀一致】

【这里也就是说我配置中心中有一个名为nacos-config-dev.yaml的文件】

编写application.yml
server:
  port: 3377
spring:
  profiles:
    active: dev
测试
@RestController
@RefreshScope // 支持动态刷新功能
public class NacosConfigClientController {

    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/config/info")
    public String getConfigInfo() {
        return configInfo;
    }
}

5.三元组

命名空间 (Namespace)

命名空间用于隔离不同环境或项目的配置。你可以为不同的环境创建不同的命名空间。

config:
  server-addr: localhost:8848
  file-extension: yaml
  namespace: 命名空间id
组 (Group)

组用于对配置进行分类管理。默认的组名为 DEFAULT_GROUP,你也可以根据需要自定义组名。

config:
  server-addr: localhost:8848
  file-extension: yaml
  group: PROD_GROUP
数据 ID (Data ID)

就是根据nacos中nacos-config-dev.yaml,nacos-config-test.yaml…

学习地址

https://www.bilibili.com/video/BV1gW421P7RD?p=99&spm_id_from=pageDriver&vd_source=7268b1c733746031a0c523593f1e1f31



第三章 Sentinel流量组件

1.介绍

Sentinel 是阿里巴巴开源的一款面向分布式服务架构的流量防卫组件。它主要用于保障微服务架构中的稳定性和可靠性,通过对流量进行控制、熔断、降级等手段,防止服务在高并发、流量突增等情况下出现崩溃或严重性能问题。

2.主要功能

流量控制(限流):根据配置限制每秒请求数或并发数,防止服务因流量过大而崩溃。

熔断降级:当服务出现高延迟或错误率过高时,自动停止对该服务的请求,以保护系统稳定。

系统负载保护:监控系统整体负载情况,当负载过高时自动减少流量,避免系统崩溃。

热点参数限流:对特定请求参数的高频请求进行限流,防止单一资源的过度消耗。

实时监控和报警:提供实时监控服务运行情况,并在异常时发送报警通知。

3.纳入Sentinel监控

server:
  port: 8401
spring:
  application:
    name: cloud-alibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport: 
        dashboard: localhost:8080 #Sentinel本地端口
        port: 8719 # 通信的本地端口

4.流控模式

【在Sentinel左边菜单栏的流控规则中进行配置】

流控模式
直接

基本的限流方式,它直接基于流量的数量来进行限流。

在这种模式下,当流量超过设定的阈值时,系统会立即对超出的请求进行限制。

关联

关联模式通过关联其他资源的流量来决定当前资源的流控策略。

即当关联的资源达到阈值时,当前资源也会触发限流。

链路

链路模式是一种更复杂的限流方式,它基于调用链路来进行限流。只有当指定的链路(即从调用源头到目标资源的一系列调用路径)上的流量超过阈值时,才会触发限流。这种模式适用于服务调用路径比较复杂的分布式系统中,可以精准控制特定调用链上的流量。

【https://www.bilibili.com/video/BV1gW421P7RD?p=106】

流控效果
快速失效

快速失效字面意思就是快速失效

Warm Up(预热)

系统启动当系统刚启动时,可以使用预热机制逐步增加流量,以确保系统稳定。

【预热时长10意思就是在10秒内系统还处于冷启动有的请求会丢弃】

排队等待

排队等待指的是将请求或任务放入队列中,以便系统能够逐步处理这些请求,防止系统因过载而崩溃。

阈值的作用:你设定的 QPS 阈值为 50,意味着每台机器每秒最多允许 50 个请求通过。如果在某个时间段内,请求量超过了这个阈值,那么超过的请求不会被立即拒绝,而是进入一个排队等待的队列。

超时时间(这里是 1000 毫秒)表示请求在队列中最多可以等待的时间。如果请求在队列中等待的时间超过了这个超时时间,它将被丢弃或返回错误。



第四章 Sentinel熔断组件

1.熔断总配置

资源名

资源名 /testF 是要应用熔断规则的资源名称,通常是某个服务的接口路径。

熔断策略
  • 慢调用比例:基于请求的响应时间来决定是否触发熔断。当响应时间超过设定的最大 RT(Response Time,响应时间)时,如果慢调用的比例超过了设定的阈值(比例阈值),就会触发熔断。

  • 异常比例:基于请求的异常比例(失败率)来决定是否触发熔断。如果异常比例超过设定的阈值,就会触发熔断。

  • 异常数:基于请求的异常次数来决定是否触发熔断。如果异常次数达到设定的阈值,就会触发熔断。

最大 RT

设定的最大响应时间为 200 毫秒。如果某个请求的响应时间超过 200 毫秒,就会被视为一次“慢调用”。

比例阈值

比例阈值为 0.1(即 10%)。在“慢调用比例”策略下,如果慢调用的比例超过 10%,就会触发熔断。

熔断时长

熔断时长设定为 5 秒,表示当触发熔断后,该资源将被熔断 5 秒。在熔断期间,所有对该资源的请求将被拒绝。

最小请求数

最小请求数为 5,表示只有在统计周期内的请求数达到 5 次后,才会根据熔断策略进行熔断判断。如果请求数不足 5 次,熔断规则不会生效。

统计时长

统计时长为 1000 毫秒(1 秒),表示熔断策略将在这个时间段内进行统计和评估。



第五章 @SentinelResource注解

1.默认不使用

@GetMapping("/rateLimitByUrl")
public String rateLimitByUrl() {
    return "限流测试未使用注解";
}

【如果没有使用注解就要和Sentinel中的新增流控规则资源名相同,比如路径名叫/rateLimitByUrl,资源名也要一样】

2.开启使用blockHandler

@GetMapping("/rateLimitByResource")
@SentinelResource(value = "ByResource", blockHandler = "byResourceHandler")
public String rateLimitByResource() {
    return "限流测试使用注解, 返回指定的字符串";
}

public String byResourceHandler(BlockException blockException) {
    return "服务不可用, 这是自定义返回的字符串";
}

【@SentinelResource中value就是新增流控规则资源名,必须Sentinel中要有这个流控,如果限流就会执行byResourceHandler方法】

3.开启使用blockHandler

@GetMapping("ce1/{shu}")
@SentinelResource(value = "ce",blockHandler = "byResourceHandler",fallback = "byFallback")
public String ce1(@PathVariable("shu") String shu){
    if (Integer.parseInt(shu)==0){
        throw new  RuntimeException();
    }
    System.out.println(shu);
    return shu;
}

public String byFallback(@PathVariable("shu") String shu, Throwable throwable) {
    return "逻辑异常, 这是自定义返回的字符串";
}


public String byResourceHandler(@PathVariable("shu") String shu,BlockException blockException) {
    return "限流了..........";
}

【blockHandler】:用来处理限流后的操作

【byFallback】:用来处理异常后的操作

【注意】:blockHandler、byFallback中的参数必须要和原始方法相匹配,blockHandler的BlockException blockException参数也是必须的



第六章 Sentinel热点规则

资源名

testHotKey: 这是 Sentinel 识别和应用流量控制规则的标识。你需要确保在代码中的 @SentinelResource 注解的 value 属性和这里的资源名匹配。

限流模式

QPS 模式: 表示以每秒请求数(Queries Per Second, QPS)作为限流的标准。这是常用的限流模式,适用于高流量的场景。

参数索引

指定了热点参数在请求中的索引位置。Sentinel 会基于这个位置的参数值进行限流。例如,如果设置为0,Sentinel 会将第一个参数作为热点参数进行控制。

单机阈值

设置在单台机器上的流量控制阈值。例如,如果设置为1,则当单机的请求超过这个阈值时,Sentinel 会触发流量控制。

统计窗口时长

1 秒: 指定了用于计算流量控制的统计窗口的时长。Sentinel 会在这个时间窗口内收集请求数据,并应用流量控制规则。1秒的窗口时长意味着流量控制规则会每秒钟更新一次。

是否集群

是否集群: 如果选择了集群模式,那么流量控制规则会在集群中所有节点上生效。如果不选择集群模式,那么流量控制只会在当前单机上生效。

参数例外项

参数例外项: 用于指定哪些特定的参数值应当被排除在流量控制之外。例如,如果你想允许某些参数值超出限流阈值而不受限制,可以在这里配置这些例外项。

参数类型

java.lang.String: 指定了热点参数的类型。在这个例子中,参数类型为 String,意味着 Sentinel 会将参数视为字符串类型来进行流量控制。

参数值

指定了例外项的参数值。如果设置了例外项参数值 w,那么当请求参数值为 w 时,该请求将不会受到流量控制的影响。

限流阈值

200: 设置了流量控制的阈值。例如,如果设置为200,则表示每秒钟最多允许200个请求,如果超过这个阈值,则触发限流策略。



第七章 Sentinel整合OpenFeign、gateway

Sentinel整合OpenFeign

依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
开启整合
#开启对sentinel的整合
feign:
  sentinel:
    enabled: true

【在consumer(消费者)模块的yml中添加】

feign接口编写
@FeignClient(value = "nacos-pay-provider"
        , fallback = PayFeignSentinelFallback.class 
)  //fallback降级类
public interface PayFeignSentinelApi {

    @GetMapping("/pay/nacos/get/{orderNo}")
    Result<PayDTO> getPayByOrderNo(@PathVariable("orderNo") String orderNo);

}
降级处理
@Component
public class PayFeignSentinelFallback implements PayFeignSentinelApi {
    @Override
    public Result<PayDTO> getPayByOrderNo(String orderNo) {
        return Result.error(ReturnCodeEnum.RC500.getCode(), "对方服务不可用!!");
    }
}

Sentinel整合gateway

依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.8.6</version>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
    <version>1.8.6</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
    <scope>compile</scope>
</dependency>
yml编写
server:
  port: 9528

spring:
  application:
    name: cloud-alibaba-sentinel-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # nacos地址
    gateway:
      routes:
        - id: pay_routh1
          uri: http://localhost:9001 #使用url必须添加http://  还可以使用lb://nacos-pay-provider服务名的方式
          predicates:
            - Path=/pay/nacos/get/**  #谓词的配置
创建网关配置类
@Configuration
public class GatewayConfiguration {
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolvers;
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit() {
        initBlockHandler();
    }

    private void initBlockHandler() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(
                new GatewayFlowRule("pay_routh1") //这里是路由的id是在yml中
                        .setCount(1) //设置最大访问
                        .setIntervalSec(1) //设置间隔秒
        );
        GatewayRuleManager.loadRules(rules);  //加载

        BlockRequestHandler handler = (serverWebExchange, throwable) -> {
            HashMap<String, String> map = new HashMap<>();

            map.put("ErrorCode", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
            map.put("ErrorMessage", "请求过于频繁, 触发了sentinel限流 ... ");

            return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(map));
        };

        GatewayCallbackManager.setBlockHandler(handler);
    }
}
测试

发送请求到网关,网关根据

  new GatewayFlowRule("pay_routh1") //这里是路由的id是在yml中
                        .setCount(1) //设置最大访问
                        .setIntervalSec(1) //设置间隔秒

来决定是否放行



第八章 Seata分布式事务

1.介绍

Seata是一个开源的分布式事务解决方案,主要用于解决微服务架构中跨服务的数据一致性问题。Seata 提供了高效且易用的分布式事务处理能力,确保在分布式系统中进行的数据操作能够保持一致性。

2.组成部分

TC

事务协调器,管理全局事务的生命周期,负责全局事务的开始、提交、回滚等操作。

TM

事务管理器,负责定义全局事务的范围,触发全局事务的开始和提交/回滚

RM

资源管理器,负责管理分支事务,参与具体的业务操作,并与 TC 进行通讯来实现分支事务的注册和状态汇报。

【其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。】

3.事务模式

AT 模式
  • AT 模式是一种自动化的分布式事务模式,能够自动管理本地数据库事务。Seata 在两阶段提交(2PC)协议的基础上,增强了性能和可用性:
    • 第一阶段:在业务执行过程中,RM 会生成一份数据的“镜像”用于后续的回滚,同时提交本地事务并释放数据库锁。
    • 第二阶段:TC 根据事务的最终状态(提交或回滚)来决定是否需要回滚前一阶段的操作。
TCC 模式
  • TCC(Try-Confirm-Cancel)模式,要求业务服务提供 Try、Confirm 和 Cancel 三个接口,分别对应预操作、确认操作和取消操作。
SAGA 模式
  • SAGA 模式适合长事务处理,将全局事务拆分为一系列的本地事务,每个本地事务具有对应的补偿机制(回滚操作)。
XA 模式
  • XA 模式遵循标准的 2PC 协议,由数据库本身支持 XA 事务管理,适用于需要严格强一致性的场景。

4.项目实战

添加依赖
<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>
    <!-- 引入自定义api通用库-->
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>cloud-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>


    <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>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
    </dependency>
    <!-- http://localhost:8001/swagger-ui/index.html -->
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>javax.persistence</groupId>
        <artifactId>persistence-api</artifactId>
    </dependency>
    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper</artifactId>
    </dependency>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
        <version>1.18.30</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>


    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.4.1</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>3.0.3</version>
    </dependency>
</dependencies>
编写数据库
seata 数据库
/*
 Navicat Premium Data Transfer

 Source Server         : mysql
 Source Server Type    : MySQL
 Source Server Version : 80028 (8.0.28)
 Source Host           : localhost:3306
 Source Schema         : seata

 Target Server Type    : MySQL
 Target Server Version : 80028 (8.0.28)
 File Encoding         : 65001

 Date: 18/08/2024 22:32:47
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for branch_table
-- ----------------------------
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table`  (
  `branch_id` bigint NOT NULL,
  `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `transaction_id` bigint NULL DEFAULT NULL,
  `resource_group_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `resource_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `branch_type` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `status` tinyint NULL DEFAULT NULL,
  `client_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `application_data` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `gmt_create` datetime(6) NULL DEFAULT NULL,
  `gmt_modified` datetime(6) NULL DEFAULT NULL,
  PRIMARY KEY (`branch_id`) USING BTREE,
  INDEX `idx_xid`(`xid` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of branch_table
-- ----------------------------

-- ----------------------------
-- Table structure for distributed_lock
-- ----------------------------
DROP TABLE IF EXISTS `distributed_lock`;
CREATE TABLE `distributed_lock`  (
  `lock_key` char(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `lock_value` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `expire` bigint NULL DEFAULT NULL,
  PRIMARY KEY (`lock_key`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of distributed_lock
-- ----------------------------
INSERT INTO `distributed_lock` VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` VALUES ('TxTimeoutCheck', ' ', 0);

-- ----------------------------
-- Table structure for global_table
-- ----------------------------
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table`  (
  `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `transaction_id` bigint NULL DEFAULT NULL,
  `status` tinyint NOT NULL,
  `application_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `transaction_service_group` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `transaction_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `timeout` int NULL DEFAULT NULL,
  `begin_time` bigint NULL DEFAULT NULL,
  `application_data` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `gmt_create` datetime NULL DEFAULT NULL,
  `gmt_modified` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`xid`) USING BTREE,
  INDEX `idx_status_gmt_modified`(`status` ASC, `gmt_modified` ASC) USING BTREE,
  INDEX `idx_transaction_id`(`transaction_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of global_table
-- ----------------------------

-- ----------------------------
-- Table structure for lock_table
-- ----------------------------
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table`  (
  `row_key` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `transaction_id` bigint NULL DEFAULT NULL,
  `branch_id` bigint NOT NULL,
  `resource_id` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `table_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `pk` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `status` tinyint NOT NULL DEFAULT 0 COMMENT '0:locked ,1:rollbacking',
  `gmt_create` datetime NULL DEFAULT NULL,
  `gmt_modified` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`row_key`) USING BTREE,
  INDEX `idx_status`(`status` ASC) USING BTREE,
  INDEX `idx_branch_id`(`branch_id` ASC) USING BTREE,
  INDEX `idx_xid`(`xid` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of lock_table
-- ----------------------------

SET FOREIGN_KEY_CHECKS = 1;

seata_account 账户数据库
/*
 Navicat Premium Data Transfer

 Source Server         : mysql
 Source Server Type    : MySQL
 Source Server Version : 80028 (8.0.28)
 Source Host           : localhost:3306
 Source Schema         : seata_account

 Target Server Type    : MySQL
 Target Server Version : 80028 (8.0.28)
 File Encoding         : 65001

 Date: 18/08/2024 22:33:09
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_account
-- ----------------------------
DROP TABLE IF EXISTS `t_account`;
CREATE TABLE `t_account`  (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
  `user_id` bigint NULL DEFAULT NULL COMMENT '用户id',
  `total` decimal(10, 0) NULL DEFAULT NULL COMMENT '总额度',
  `used` decimal(10, 0) NULL DEFAULT NULL COMMENT '已用账户余额',
  `residue` decimal(10, 0) NULL DEFAULT NULL COMMENT '剩余可用额度',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_account
-- ----------------------------
INSERT INTO `t_account` VALUES (1, 1, 1000, 0, 1000);

-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (
  `branch_id` bigint NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'undo_log context,such as serialization',
  `rollback_info` longblob NOT NULL COMMENT 'rollback info',
  `log_status` int 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 INDEX `ux_undo_log`(`xid` ASC, `branch_id` ASC) USING BTREE,
  INDEX `ix_log_created`(`log_created` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of undo_log
-- ----------------------------

SET FOREIGN_KEY_CHECKS = 1;

seata_order 订单数据库
/*
 Navicat Premium Data Transfer

 Source Server         : mysql
 Source Server Type    : MySQL
 Source Server Version : 80028 (8.0.28)
 Source Host           : localhost:3306
 Source Schema         : seata_order

 Target Server Type    : MySQL
 Target Server Version : 80028 (8.0.28)
 File Encoding         : 65001

 Date: 18/08/2024 22:33:14
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_order
-- ----------------------------
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order`  (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `user_id` bigint NULL DEFAULT NULL,
  `product_id` bigint NULL DEFAULT NULL,
  `count` int NULL DEFAULT NULL,
  `moeny` int NULL DEFAULT NULL,
  `status` int NULL DEFAULT NULL COMMENT '订单状态:0创建中:1已完结',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_order
-- ----------------------------

-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (
  `branch_id` bigint NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'undo_log context,such as serialization',
  `rollback_info` longblob NOT NULL COMMENT 'rollback info',
  `log_status` int 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 INDEX `ux_undo_log`(`xid` ASC, `branch_id` ASC) USING BTREE,
  INDEX `ix_log_created`(`log_created` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of undo_log
-- ----------------------------

SET FOREIGN_KEY_CHECKS = 1;

seata_storage 库存数据库
/*
 Navicat Premium Data Transfer

 Source Server         : mysql
 Source Server Type    : MySQL
 Source Server Version : 80028 (8.0.28)
 Source Host           : localhost:3306
 Source Schema         : seata_storage

 Target Server Type    : MySQL
 Target Server Version : 80028 (8.0.28)
 File Encoding         : 65001

 Date: 18/08/2024 22:33:20
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_storage
-- ----------------------------
DROP TABLE IF EXISTS `t_storage`;
CREATE TABLE `t_storage`  (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
  `product_id` bigint NULL DEFAULT NULL COMMENT '产品id',
  `total` decimal(10, 0) NULL DEFAULT NULL COMMENT '总库存',
  `used` decimal(10, 0) NULL DEFAULT NULL COMMENT '已用库存',
  `residue` decimal(10, 0) NULL DEFAULT NULL COMMENT '剩余库存',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_storage
-- ----------------------------
INSERT INTO `t_storage` VALUES (1, 1, 100, 0, 100);

-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (
  `branch_id` bigint NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'undo_log context,such as serialization',
  `rollback_info` longblob NOT NULL COMMENT 'rollback info',
  `log_status` int 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 INDEX `ux_undo_log`(`xid` ASC, `branch_id` ASC) USING BTREE,
  INDEX `ix_log_created`(`log_created` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of undo_log
-- ----------------------------

SET FOREIGN_KEY_CHECKS = 1;

修改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.xml
  file:
    path: ${log.home:${user.home}/logs/seata}
  extend:
    logstash-appender:
      destination: 127.0.0.1:4560
    kafka-appender:
      bootstrap-servers: 127.0.0.1:9092
      topic: logback_to_logstash

console:
  user:
    username: seata
    password: seata
seata:
  config:
    # support: nacos, consul, apollo, zk, etcd3
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: public
      group: DEFAULT_GROUP
      username: nacos
      password: nacos
  registry:
    # support: nacos, eureka, redis, zk, consul, etcd3, sofa
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: DEFAULT_GROUP
      namespace: public
      cluster: default
      username: nacos
      password: nacos
  store:
    # support: file 、 db 、 redis 、 raft
    mode: db
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/seata?serverTimezone=Asia/Shanghai&useSSL=false
      user: root
      password: ok
      min-conn: 10
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 1000
      max-wait: 5000




  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login,/metadata/v1/**

编写yml文件
server:
  port: 2003

spring:
  application:
    name: seata-account-service
  cloud:
    nacos:
      server-addr: localhost:8848
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_account?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
    username: root
    password: ok

seata:
  registry:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: ""
      group: DEFAULT_GROUP
      application: seata-server
  tx-service-group: default_tx_group #这里是组的id用于标识和管理分布式事务的全局事务组。 
  service:
    vgroup-mapping:
      default_tx_group: default #默认default
  data-source-proxy-mode: AT

logging:
  level:
    io:
      seata: info

编写业务
订单业务
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author author
 * @since 2024-08-18
 */
@Service
@Slf4j
public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements ITOrderService {

    @Resource
    private TOrderMapper orderMapper;
    @Resource
    private StorageFeignApi storageFeignApi;
    @Resource
    private AccountFeignApi accountFeignApi;

    @Override
    public void create(TOrder order) {
        // xid全局事务检查
        String xid = RootContext.getXID();

        // 1. 新建订单
        log.info("-------------> 开始新建订单, XID: {}", xid);
        order.setStatus(0);
        int result = orderMapper.insert(order);

        TOrder orderFromDB;
        if (result > 0) {

            System.out.println(order.getId());
            orderFromDB = orderMapper.selectById(order.getId());
            log.info("-------------> 新建订单成功, OrderInfo: {}", orderFromDB);

            // 2. 扣减库存
            log.info("-------------> 开始扣减库存");
            storageFeignApi.decrease(orderFromDB.getProductId(), orderFromDB.getCount());
            log.info("-------------> 扣减库存成功");

            // 3. 扣减账户余额
            log.info("-------------> 开始扣减余额");
            accountFeignApi.decrease(order.getUserId(), Long.valueOf(order.getMoeny()));
            log.info("-------------> 扣余额存成功");

            // 4. 修改订单状态
            log.info("-------------> 开始修改订单状态");
            order.setId(orderFromDB.getId());
            order.setStatus(1);
            int i = orderMapper.updateById(order);
            log.info("-------------> 修改订单状态成功");

        }
        log.info("-------------> 结束新建订单, XID: {}", xid);
    }
}
账户业务
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author author
 * @since 2024-08-18
 */
@Slf4j
@Service
public class TAccountServiceImpl extends ServiceImpl<TAccountMapper, TAccount> implements ITAccountService {

    @Resource
    private TAccountMapper accountMapper;

    @Override
    public void decrease(Long userId, Long money) {
        log.info("------------->AccountService 开始扣减余额");
        accountMapper.decrease(userId, money);
        log.info("------------->AccountService 开始扣减余额");


        // 超时异常
         timeout();
        // 抛出异常
//         int i = 10 / 0;
    }

    private void timeout() {
        try {
            TimeUnit.SECONDS.sleep(65);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

库存业务
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author author
 * @since 2024-08-18
 */
@Slf4j
@Service
public class TAccountServiceImpl extends ServiceImpl<TAccountMapper, TAccount> implements ITAccountService {

    @Resource
    private TAccountMapper accountMapper;

    @Override
    public void decrease(Long userId, Long money) {
        log.info("------------->AccountService 开始扣减余额");
        accountMapper.decrease(userId, money);
        log.info("------------->AccountService 开始扣减余额");


        // 超时异常
         timeout();
        // 抛出异常
//         int i = 10 / 0;
    }

    private void timeout() {
        try {
            TimeUnit.SECONDS.sleep(65);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

测试·总结

【控制器直接调用 ,OpenFeign也一样】

【通过添加订单进行分布式事务的测试】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值