【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata

news/2024/4/26 14:45:07/文章来源:https://blog.csdn.net/weixin_43025151/article/details/130301010

Nacos+Sentinel+Seata 9/9

    • 1、SpringCloud Alibaba简介
      • 1.1 主要功能
      • 1.2 具体组件
    • 2、SpringCloud Alibaba Nacos服务注册和配置中心
      • 2.1 Nacos介绍
      • 2.2 Nacos下载安装
      • 2.3 使用Nacos作为注册中心
        • 2.3.1 在父工程的pom文件中引入springcloudalibaba依赖
        • 2.3.2 创建cloudalibaba-provider-payment9001模块
        • 2.3.3 创建cloudalibaba-provider-payment9002模块
        • 2.3.4 创建cloudalibaba-consumer-nacos-order83模块
        • 2.3.5 Nacos与其他服务注册的对比
      • 2.4 使用Nacos作为配置中心
        • 2.4.1创建cloudalibaba-config-nacos-client-3377模块(配置中心的客户端模块)
        • 2.4.2 Nacos默认就开启了自动刷新
        • 2.4.3 Nacos配置中心之分类配置:
          • 2.4.3.1 分类配置简介
          • 2.4.3.2 DataId配置方案
          • 2.4.3.3 GroupID配置方案
          • 2.4.3.4 namespace配置方案
      • 2.5 Nacos集群和持久化配置:
        • 2.5.1 单机版,切换mysql数据库
        • 2.5.2 Linux上配置Nacos集群+Mysql数据库
          • 2.5.2.1 Nacos下载Linux版
          • 2.5.2.2 集群配置步骤(重点)
    • 3、SpringCloud Alibaba Sentinel实现熔断与限流
      • 3.1 什么是Sentinel
      • 3.2 安装Sentinel控制台
      • 3.2 微服务整合sentinel
        • 3.2.1 启动Nacos 8848
        • 3.2.2 新建一个cloudalibaba-sentinel-service8401模块
        • 3.2.3 Sentinel的概念和功能
          • 3.2.3.1 基本概念
          • 3.2.3.2 重要功能
        • 3.2.4 sentinel的流控规则
          • 3.2.4.1 简单配置
          • 3.2.4.2 配置流控模式
          • 3.2.4.3 配置流控效果
        • 3.2.5 sentinel的降级规则:
          • 3.2.5.1 RT配置:
          • 3.2.5.2 异常比例:
          • 3.2.5.3 异常数:
        • 3.2.6 sentinel的热点规则:
          • 3.2.6.1 热点规则简单使用
          • 3.2.6.2 设置热点规则中的其他选项:
        • 3.2.7 sentinel的系统规则:
          • 3.2.7.1 @SentinelResource注解:
            • 3.2.7.1.1 **自定义限流处理逻辑:**
          • 3.2.7.2 @SentinelResource注解的其他属性:
        • 3.2.8 服务熔断:
          • 3.2.8.1 **启动nacos和sentinel**
          • 3.2.8.2 **新建两个pay模块 9003和9004**
          • 3.2.8.3 **新建一个order-84消费者模块:**
        • 3.2.9 sentinel整合ribbon+openFeign+fallback
        • 3.2.10 sentinel持久化规则
    • 4、SpringCloud Alibaba Seata处理分布式事务
      • 4.1 什么是分布式事务
      • 4.2 分布式事务中的一些概念
      • 4.3 seata安装和启动:
      • 4.3 商品交易案例
        • 4.3.1 业务说明
        • 4.3.2 创建三个数据库
        • 4.3.3 创建对应的表以及创建回滚日志表,方便查看
        • 4.3.4 创建微服务模块
          • 4.3.4.1 订单模块seata-order-service2001
          • 4.3.4.2 库存模块seata-storage-service2002
          • 4.3.4.3 账号模块seata-account-service2003
          • 4.3.4.4 数据库初始情况
          • 4.3.4.5 正常下单
          • 4.3.4.6 超时异常,没加@GlobalTransactional
          • 4.3.4.7 超时异常,添加@GlobalTransactional
      • 4.4 setat原理

1、SpringCloud Alibaba简介

Spring Cloud Netflix项目进入维护模式,不再更新开发新组件了

Dubbo 也不再维护和更新

需要替代方案,Spring Cloud Alibaba 应用而生

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。

此外,阿里云同时还提供了 Spring Cloud Alibaba 企业版 微服务解决方案,包括无侵入服务治理(全链路灰度,无损上下线,离群实例摘除等),企业级 Nacos 注册配置中心和企业级云原生网关等众多产品。

1.1 主要功能

  • 服务限流降级:默认支持 WebServlet、WebFlux、OpenFeign、RestTemplate、Spring Cloud Gateway、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
  • 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成对应 Spring Cloud 版本所支持的负载均衡组件的适配。
  • 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
  • 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
  • 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。
  • 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
  • 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

几乎可以将之前的Spring Cloud代替

除了上述所具有的功能外,针对企业级用户的场景,Spring Cloud Alibaba 配套的企业版微服务治理方案 微服务引擎MSE 还提供了企业级微服务治理中心,包括全链路灰度、服务预热、无损上下线和离群实例摘除等更多更强大的治理能力,同时还提供了企业级 Nacos 注册配置中心,企业级云原生网关等多种产品及解决方案。

1.2 具体组件

Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。

Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。

Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。

Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。

Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

2、SpringCloud Alibaba Nacos服务注册和配置中心

2.1 Nacos介绍

为什么叫Nacos:前四个字母分别为naming和Configuration的前两个字母,最后的s为service ;一个更易于构建云原生应用的动态服务发现,配置管理和服务管理平台。

==Nacos:Dynamic Naming and Configuration Service ==

服务注册和配置中心的组合

== Nacos=erueka+config+bus==

  • 替代Eureka做服务注册中心
  • 替代Config做服务配置中心

Nacos官网地址:https://nacos.io/zh-cn/

各种注册中心比较 :

在这里插入图片描述
Nacos在阿里巴巴内部有超过10万的实例运行,已经过了类似双十一等各种大型流量的考验。

2.2 Nacos下载安装

Github下载地址:https://github.com/alibaba/nacos/releases/tag/2.2.2

安装Nacos:

本地java8+maven环境已经ok

1. 到github上下载安装包

解压安装包

2. 启动Nacos

解压安装包,直接运行bin目录下的startup.cmd
在这里插入图片描述

3. 访问Nacos

命令运行成功后直接访问http://localhost:8848/nacos/index.html

账号密码:默认都是nacos

结果页面:
在这里插入图片描述

在这里插入图片描述

2.3 使用Nacos作为注册中心

2.3.1 在父工程的pom文件中引入springcloudalibaba依赖

            <!--  spring cloud alibaba 2.1.0.RELEASE    --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.1.0.RELEASE</version><type>pom</type><scope>import</scope></dependency>

2.3.2 创建cloudalibaba-provider-payment9001模块

  1. 新建cloudalibaba-provider-payment9001模块
  2. 修改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-tigerhhzz</artifactId><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>cloudalibaba-provider-payment9001</artifactId><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></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>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
</project>
  1. 配置文件
server:port: 9001
spring:application:name: nacos-payment-providercloud:nacos:discovery:server-addr: localhost:8848
management:endpoints:web:exposure:include: "*"
  1. 主启动类
package com.tigerhhzz.springcloud;import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;/*** @author tigerhhzz* @date 2023/4/18 19:49*/
@Slf4j
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderMain9001 {public static void main(String[] args) {SpringApplication.run(NacosProviderMain9001.class,args);log.info("NacosProviderMain9001启动成功~~~~~~~~~~~~~");}
}
  1. 业务类controller:
package com.tigerhhzz.springcloud.controller;import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author tigerhhzz* @date 2023/4/18 19:50*/
@RestController
@RequestMapping("/payment")
public class PaymentController {@Value("${server.port}")public String serverPort;@RequestMapping("/getPayment/{id}")public String getPayment(@PathVariable("id") Integer id){return "Alibaba Nacos server "+ serverPort+"-----"+id;}}
  1. 测试

启动cloudalibaba-provider-payment9001

然后查看Nacos的web界面,可以看到nacos-payment-provider已经注册成功
在这里插入图片描述

2.3.3 创建cloudalibaba-provider-payment9002模块

创建过程雷同9001模块。

2.3.4 创建cloudalibaba-consumer-nacos-order83模块

  1. 新建cloudalibaba-consumer-nacos-order83模块
  2. 修改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-tigerhhzz</artifactId><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>cloudalibaba-consumer-nacos-order83</artifactId><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></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>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>
  1. 配置文件
server:port: 83spring:application:name: cloud-nacos-ordercloud:nacos:discovery:server-addr: localhost:8848#消费者将要去访问的微服务名称
server-url:nacos-user-service: http://nacos-payment-provider
  1. 主启动类
package com.tigerhhzz.springcloud;import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;/*** @author tigerhhzz* @date 2023/4/18 20:24*/
@Slf4j
@SpringBootApplication
@EnableDiscoveryClient
public class NacosOrderMain83 {public static void main(String[] args) {SpringApplication.run(NacosOrderMain83.class,args);log.info("NacosOrderMain83启动成功~~~~~~~~~~~~~");}
}
  1. 业务类controller:
package com.tigerhhzz.springcloud.controller;import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import javax.annotation.Resource;/*** @author tigerhhzz* @date 2023/4/18 20:30*/
@RestController
public class OrderNacosController {@Resourceprivate RestTemplate restTemplate;@Value("${server-url.nacos-user-service}")private String url;@GetMapping("/order/getPayment/{id}")public String getPaymentInfo(@PathVariable("id") Long id) {return restTemplate.getForObject(url+"/payment/getPayment/"+id,String.class);}
}
  1. 编写配置类

因为Naocs要使用Ribbon进行负载均衡,那么就需要使用RestTemplate

package com.tigerhhzz.springcloud.config;import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;/*** @author tigerhhzz* @date 2023/4/18 20:29*/
@Configuration
public class ApplicationContextConfig {@Bean@LoadBalanced  //负载均衡:轮询public RestTemplate getRestTemplate(){return new RestTemplate();}
}
  1. 测试

启动cloudalibaba-consumer-nacos-order83模块
启动cloudalibaba-provider-payment9001模块
启动cloudalibaba-provider-payment9002模块
在这里插入图片描述
访问: http://localhost:83/order/getPayment/11

可以看到,实现了负载均衡

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A4wOTCPA-1682128319805)(C:\Users\Administrator\AppData\Local\Temp\1681961592544.png)]

Nacos天生就自带netflix-ribbon负载均衡功能,因为它整合了netflix-ribbon依赖:

在这里插入图片描述

2.3.5 Nacos与其他服务注册的对比

Nacos它既可以支持CP,也可以支持AP,可以切换
在这里插入图片描述

下面这个curl命令,就是切换模式, 在CP与AP之间切换

在这里插入图片描述

2.4 使用Nacos作为配置中心

2.4.1创建cloudalibaba-config-nacos-client-3377模块(配置中心的客户端模块)

  1. 新建cloudalibaba-config-nacos-client-3377模块
  2. 修改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-tigerhhzz</artifactId><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>cloudalibaba-config-nacos-client-3377</artifactId><dependencies><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><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>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>
  1. 配置文件

在这里插入图片描述
这里需要配置两个配置文件,application.yml和bootstarp.yml

主要是为了可以与spring clodu config无缝迁移

bootstarp.yml

server:port: 3377spring:application:name: nacos-config-clientcloud:nacos:discovery:server-addr: localhost:8848  #Nacos服务注册中心地址config:server-addr: localhost:8848  #Nacos作为配置中心地址file-extension: yml #指定yaml格式的配置#namespace: 7a901d46-e75e-4e6a-b186-5980cca4249bgroup: TEST_GROUP   #TEST_GROUP  DEV_GROUP# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
#nacos-config-client-dev.yaml

application.yml

spring:profiles:#active: dev  #开发环境#active: test  #测试环境active: INFO  #测试环境
  1. 主启动类
package com.tigerhhzz.springcloud;import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;/*** @author tigerhhzz* @date 2023/4/18 21:03*/
@Slf4j
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigNacosMain3377 {public static void main(String[] args) {SpringApplication.run(ConfigNacosMain3377.class,args);log.info("ConfigNacosMain3377启动成功~~~~~~~~~~~~~");}
}
  1. 业务类controller
package com.tigerhhzz.springcloud.controller;import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author tigerhhzz* @date 2023/4/18 21:04*/
@RestController
@RefreshScope  //支持nacos的动态刷新功能
public class ConfigClientController {@Value("${config.info}")private String configInfo;@GetMapping("/config/info")public String getConfigInfo() {return configInfo;}
}

通过springloud的原生注解@RefreshScope,实现了配置自动刷新。

  1. 在nacos中添加配置信息

在这里插入图片描述

Nacos的配置规则:

理论:Nacos中的dataid的组成格式及与SpringBoot配置文件中的配置规则。
https://nacos.io/zh-cn/docs/what-is-nacos.html

配置规则,就是我们在客户端如何指定读取配置文件,配置文件的命名的规则

默认的命名方式:

prefix:默认就是当前服务的服务名称也可以通过spring.cloud.necos.config.prefix配置spring.profile.active:就是我们在application.yml中指定的,当前是开发环境还是测试等环境这个可以不配置,如果不配置,那么前面的-也会没有file-extension就是当前文件的格式(后缀),目前只支持yml和properties

在这里插入图片描述

注意,DataId就是配置文件名字:

名字一定要按照上面的规则命名,否则客户端会读取不到配置文件

  1. 测试

重启cloudalibaba-config-nacos-client-3377客户端

调用接口查看配置信息 http://localhost:3377/config/info

在这里插入图片描述

拿到了配置文件中的值

2.4.2 Nacos默认就开启了自动刷新

此时我们修改了配置文件
在这里插入图片描述

客户端是可以立即更新的
在这里插入图片描述

因为Nacos支持Bus总线,会自动发送命令更新所有客户端

2.4.3 Nacos配置中心之分类配置:

2.4.3.1 分类配置简介

在这里插入图片描述
Namespace+Group+Data ID三者的关系?为什么这么设计?

NameSpace默认有一个:public名称空间

这三个类似java的: 包名 + 类名 + 方法名

在这里插入图片描述

2.4.3.2 DataId配置方案

在这里插入图片描述

通过配置文件,实现多环境的读取:

2.4.3.3 GroupID配置方案

直接在新建配置文件时指定组
在这里插入图片描述

在客户端配置,使用指定组的配置文件:

在这里插入图片描述

2.4.3.4 namespace配置方案

客户端配置使用不同名称空间:

在这里插入图片描述

要通过命名空间id指定
修改配置文件:

bootstrap.yml

server:port: 3377spring:application:name: nacos-config-clientcloud:nacos:discovery:server-addr: localhost:8848  #Nacos服务注册中心地址config:server-addr: localhost:8848  #Nacos作为配置中心地址file-extension: yml #指定yaml格式的配置namespace: dc386f3a-1bda-4b43-8753-d3c2e4b3a187#group: TEST_GROUP   #TEST_GROUP  DEV_GROUP# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
#nacos-config-client-dev.yaml

application.yml

spring:profiles:#active: dev  #开发环境active: test  #测试环境#active: INFO  #测试环境

重启服务,OK,测试
在这里插入图片描述

2.5 Nacos集群和持久化配置:

Nacos支持三种部署模式

  • 单机模式 - 用于测试和单机试用。
  • 集群模式 - 用于生产环境,确保高可用。
  • 多集群模式 - 用于多数据中心场景。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6OpAcgdQ-1682128319874)(.\图片\Alibaba的45.png)]

2.5.1 单机版,切换mysql数据库

Nacos默认有自带嵌入式数据库,derby,但是如果做集群模式的话,就不能使用自己的数据库

不然每个节点一个数据库,那么数据就不统一了,需要使用外部的mysql

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dtRfkZBW-1682128319875)(.\图片\Alibaba的43.png)]

将nacos切换到使用我们自己的mysql数据库
在单机模式时nacos使用嵌入式数据库实现数据的存储,不方便观察数据存储的基本情况。0.7版本增加了支持mysql数据源能力,具体的操作步骤:

  1. nacos默认自带了一个sql文件,在nacos安装目录下;初始化mysql数据库,
    数据库初始化文件:mysql-schema.sql
  2. 修改conf/application.properties文件,增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码。

在这里插入图片描述

spring.datasource.platform=mysqldb.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456
  1. 再以单机模式启动nacos,nacos所有写嵌入式数据库的数据都写到了mysql

单机模式启动nacos的方法请参考https://tigerhhzz.blog.csdn.net/article/details/130308472

增加一个配置:
在这里插入图片描述

配置信息存入到mysql数据库中:
在这里插入图片描述

2.5.2 Linux上配置Nacos集群+Mysql数据库

官方架构图:

在这里插入图片描述
预计需要,1个Nginx+3nacos注册中心+1个mysql

2.5.2.1 Nacos下载Linux版

下载安装Nacos的Linux版安装包
下载地址:https://github.com/alibaba/nacos/releases/tag/2.2.2

2.5.2.2 集群配置步骤(重点)
  1. 进入安装目录,现在执行自带的sql文件

进入mysql,执行sql文件

  1. 修改配置文件,切换为我们的mysql

就是上面windos版要修改的几个属性

  1. 修改cluster.conf,指定哪几个节点是Nacos集群

这里使用3333,4444,5555作为三个Nacos节点监听的端口

在这里插入图片描述

  1. 我们这里就不配置在不同节点上了,就放在一个节点上

既然要在一个节点上启动不同Nacos实例,就要修改startup.sh,使其根据不同端口启动不同Nacos实例

在这里插入图片描述

可以看到,这个脚本就是通过jvm启动nacos

所以我们最后修改的就是,nohup java -Dserver.port=3344

  1. 配置Nginx:

在这里插入图片描述

  1. 启动Nacos:
    ./startup.sh -p 3333

./startup.sh -p 4444

./startup.sh -p 5555

  1. 启动nginx

  2. 测试:

访问192.168.159.121:1111

如果可以进入nacos的web界面,就证明安装成功了

  1. 将微服务注册到Nacos集群:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gyW9Cjat-1682128319890)(.\图片\Alibaba的51.png)]

  2. 进入Nacos的web界面

可以看到,已经注册成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xSlKAmkH-1682128319891)(.\图片\Alibaba的52.png)]
在这里插入图片描述

3、SpringCloud Alibaba Sentinel实现熔断与限流

官网链接:https://sentinelguard.io/zh-cn/docs/introduction.html

3.1 什么是Sentinel

Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量
为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即
    突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用
    应用等。
  • 完备的实时监控:Sentinel 提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒
    级数据, 甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块, 例如与 Spring
    Cloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入
    Sentinel。
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快
    速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo /
    Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等
    应用容器。

实现熔断与限流,就是阿里版的Hystrix(豪猪哥)

在这里插入图片描述

  • 服务限流
  • 服务熔断
  • 服务降级
  • 服务血崩

3.2 安装Sentinel控制台

Sentinel 提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控以及规则管理等功能。
1 下载jar包,解压到文件夹

下载地址:https://github.com/alibaba/Sentinel/releases/tag/1.8.6

  1. 运行sentinel

由于是一个jar包,所以可以直接java -jar运行

注意,默认sentinel占用8080端口

java -jar sentinel-dashboard-1.8.6.jar
  1. 访问sentinel,账号和密码均为sentinel

http://localhost:8080

在这里插入图片描述
补充:了解控制台的使用原理
Sentinel的控制台其实就是一个SpringBoot编写的程序。我们需要将我们的微服务程序注册到控制台上,
即在微服务中指定控制台的地址, 并且还要开启一个跟控制台传递数据的端口, 控制台也可以通过此端口
调用微服务中的监控程序获取微服务的各种信息.
在这里插入图片描述

3.2 微服务整合sentinel

3.2.1 启动Nacos 8848

3.2.2 新建一个cloudalibaba-sentinel-service8401模块

主要用于配置sentinel:

8401服务超过注册到nacos8848,并且被sentinel8080保护监护着,可以进行服务熔断、降级和限流的操作。

  1. 修改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-tigerhhzz</artifactId><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>cloudalibaba-sentinel-service8401</artifactId><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--        后续做持久化用到--><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></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>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
</project>
  1. 配置文件
server:port: 8401
spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:#Nacos服务注册中心地址server-addr: localhost:8848sentinel:transport:#配置Sentinel dashboard地址dashboard: localhost:8080port: 8719 #如果8719被占用,自动递增1,直到找到没有被占用的端口management:endpoints:web:exposure:include: "*"
  1. 主启动类
package com.tigerhhzz.springcloud;import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;/*** @author tigerhhzz* @date 2023/4/20 11:00*/
@Slf4j
@SpringBootApplication
@EnableDiscoveryClient
public class SentinelMain8401 {public static void main(String[] args) {SpringApplication.run(SentinelMain8401.class,args);log.info("SentinelMain8401启动成功~~~~~~~~~~~~~");}
}
  1. 业务类controller
package com.tigerhhzz.springcloud.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.tls.TlsException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.TimeUnit;/*** @author tigerhhzz* @date 2023/4/20 11:05*/
@RestController
@Slf4j
public class FlowLimitController {@RequestMapping("testA")public String testA(){return "testA-------";}@RequestMapping("testB")public String testB(){return "testB-------";}/*线程数流控测试接口*/@RequestMapping("testC")public String testC(){try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}log.info("testC 测试RT---");return "testC 测试RT-------";}@RequestMapping("testD")public String testD(){System.out.println("testD 异常比例测试");int age = 10/0;return "testD--异常比例测试\"-----";}@RequestMapping("testE")public String testE(){System.out.println("testD 异常数");int age = 10/0;return "testD 异常数-----";}@RequestMapping("testHotKey")@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKet")public String testHotKey(@RequestParam(value = "p1",required = false) String p1,@RequestParam(value = "p2",required = false) String p2){System.out.println("testHotKey 热点Key--测试");//int age = 10/0;return "testHotKey-------";}/*兜底的自定义方法*/public String deal_testHotKet(String p1, String p2, BlockException e) {return "----deal_testHotKet,------";}}
  1. 启动Sentinel8080,启动8401微服务

此时我们到sentinel中查看,发现并8401的任何信息

是因为,sentinel是懒加载,需要我们执行一次访问,才会有信息

访问localhost/8401/testA

8080Sentinel正在监视8401微服务。
在这里插入图片描述

3.2.3 Sentinel的概念和功能

3.2.3.1 基本概念
  • 资源
    资源就是Sentinel要保护的东西
    资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,可以是一个服务,也可以是
    一个方法,甚至可以是一段代码。
    我们入门案例中的message1方法就可以认为是一个资源
  • 规则
    规则就是用来定义如何进行保护资源的
    作用在资源之上, 定义以什么样的方式保护资源,主要包括流量控制规则、熔断降级规则以及系统
    保护规则。
    我们入门案例中就是为message1资源设置了一种流控规则, 限制了进入message1的流量.
3.2.3.2 重要功能

Sentinel的主要功能就是容错,主要体现为下面这三个:

  • 流量控制
    流量控制在网络传输中是一个常用的概念,它用于调整网络包的数据。任意时间到来的请求往往是
    随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。
    Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。
  • 熔断降级
    当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则
    对这个资源的调用进行限制,让请求快速失败,避影响到其它的资源而导致级联故障。

Sentinel 对这个问题采取了两种手段:

  1. 通过并发线程数进行限制
    Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。当某个资源
    出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆
    积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的
    线程完成任务后才开始继续接收请求。
  2. 通过响应时间对资源进行降级
    除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。
    当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的
    时间窗口之后才重新恢复。
SentinelHystrix 的区别
两者的原则是一致的, 都是当一个资源出现问题时, 让其快速失败, 不要波及到其它服务
但是在限制的手段上, 确采取了完全不一样的方法:
Hystrix 采用的是线程池隔离的方式, 优点是做到了资源之间的隔离, 缺点是增加了线程
切换的成本。
Sentinel 采用的是通过并发线程的数量和响应时间来对资源做限制。
  • 系统负载保护
    Sentinel 同时提供系统维度的自适应保护能力。当系统负载较高的时候,如果还持续让
    请求进入可能会导致系统崩溃,无法响应。在集群环境下,会把本应这台机器承载的流量转发到其
    它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,Sentinel 提供了对应的保
    护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请
    求。
    总之一句话: 我们需要做的事情,就是在Sentinel的资源上配置各种各样的规则,来实现各种容错的功
    能。

3.2.4 sentinel的流控规则

流量控制,其原理是监控应用流量的QPS(每秒查询率) 或并发线程数等指标,当达到指定的阈值时
对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
第1步: 点击簇点链路,我们就可以看到访问过的接口地址,然后点击对应的流控按钮,进入流控规则配
置页面。新增流控规则界面如下:
在这里插入图片描述

**资源名:**唯一名称,默认是请求路径,可自定义
针对来源:指定对哪个微服务进行限流,默认指default,意思是不区分来源,全部限制
阈值类型/单机阈值:

  • QPS(每秒请求数量): 当调用该接口的QPS达到阈值的时候,进行限流
  • 线程数:当调用该接口的线程数达到阈值的时候,进行限流
    是否集群:暂不需要集群
    接下来我们以QPS为例来研究限流规则的配置
3.2.4.1 简单配置

流控模式

我们先做一个简单配置,设置阈值类型为QPS,单机阈值为1。即每秒请求量大于1的时候开始限流。
接下来,在流控规则页面就可以看到这个配置。
然后快速访问 http://localhost:8401/testA 接口,观察效果。此时发现,当QPS > 1的时候,服务就不能正常响
应,而是返回Blocked by Sentinel (flow limiting)结果。

  1. QRS每秒请求次数

直接快速失败

在这里插入图片描述
直接失败的效果:
在这里插入图片描述
每秒请求次数超过1次,就进行流量控制。

  1. 线程数:

    比如a请求过来,处理很慢,在一直处理,此时b请求又过来了此时因为a占用一个线程,此时要处理b请求就只有额外开启一个线程那么就会报错
    

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2.4.2 配置流控模式

点击上面设置流控规则的编辑按钮,然后在编辑页面点击高级选项,会看到有流控模式一栏。
在这里插入图片描述

sentinel共有三种流控模式,分别是:

  • 直接(默认):接口达到限流条件时,开启限流
  • 关联:当关联的资源达到限流条件时,开启限流 [适合做应用让步]
  • 链路:当从某个接口过来的资源达到限流条件时,开启限流

下面呢分别演示三种模式:

  • 直接流控模式
    直接流控模式是最简单的模式,当指定的接口达到限流条件时开启限流。上面案例使用的就是直接流控
    模式。
  • 关联流控模式
    关联流控模式指的是,当指定接口关联的接口达到限流条件时,开启对指定接口开启限流。

当关联的资源达到阈值时,就限流自己。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l47nKZdR-1682128319924)(.\图片\sentinel的12.png)]

应用场景: 比如支付接口达到阈值,就要限流下订单的接口,防止一直有订单

当testA达到阈值,qps大于1,就让testB之后的请求直接失败

可以使用postman压测

访问testB成功:
在这里插入图片描述
postman里新建多线程集合组
在这里插入图片描述
访问地址添加进新线程组(20个线程每次间隔0.3秒访问一次)
在这里插入图片描述
大批量线程高并发访问B,导致A失效了

在这里插入图片描述

  • 链路流控模式
    链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流。它的功能有点类似于针对
    来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度
    更细。
3.2.4.3 配置流控效果
  • 快速失败(默认): 直接失败,抛出异常,不做任何额外的处理,是最简单的效果
  • Warm Up:它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的
    1/3,然后慢慢增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。
  • 排队等待:让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排队等待; 它还会让设
    置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。
  1. 预热Warm up:

在这里插入图片描述

  1. 排队等待

在这里插入图片描述

3.2.5 sentinel的降级规则:

就是熔断降级

降级规则就是设置当满足什么条件的时候,对服务进行降级。Sentinel提供了三个衡量条件:

  • 平均响应时间(秒级) :当资源的平均响应时间超过阈值(以 ms 为单位)之后,资源进入准降级状态。
    如果接下来 1s 内持续进入 5 个请求,它们的 RT都持续超过这个阈值,那么在接下的时间窗口
    (以 s 为单位)之内,就会对这个方法进行服务降级。
    在这里插入图片描述

==注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要
变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。 ==

  • 异常比例(秒级):当资源的每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的
    时间窗口(以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0,
    1.0]。

  • 异常数(分钟级) :当资源近 1 分钟的异常数目超过阈值之后会进行服务降级。注意由于统计时间窗口是分
    钟级别的,若时间窗口小于 60s,则结束熔断状态后仍可能再进入熔断状态。
    问题:
    流控规则和降级规则返回的异常页面是一样的,我们怎么来区分到底是什么原因导致的呢?

3.2.5.1 RT配置:

新增一个请求方法用于测试

    /*线程数流控测试接口*/@RequestMapping("testC")public String testC(){try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}log.info("testC 测试RT---");return "testC 测试RT-------";}

配置RT:

这里配置的PT,默认是秒级的平均响应时间

在这里插入图片描述

默认计算平均时间是: 1秒类进入5个请求,并且响应的平均值超过阈值(这里的200ms),就报错]

1秒5请求是Sentinel默认设置的

测试

在这里插入图片描述

默认熔断后.就直接抛出异常

3.2.5.2 异常比例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jQShUFM2-1682128319956)(.\图片\sentinel的28.png)]

修改请求方法

    @RequestMapping("testD")public String testD(){System.out.println("testD 异常比例测试");int age = 10/0;return "testD--异常比例测试\"-----";}

配置:

在这里插入图片描述

如果没触发熔断,这正常抛出异常:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tAoliL5d-1682128319961)(.\图片\sentinel的32.png)]

触发熔断:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-koFPFpiq-1682128319963)(.\图片\sentinel的33.png)]
在这里插入图片描述

3.2.5.3 异常数:

在这里插入图片描述

    @RequestMapping("testE")public String testE(){System.out.println("testD 异常数");int age = 10/0;return "testD 异常数-----";}

在这里插入图片描述

一分钟之内,有5个请求发送异常,进入熔断

3.2.6 sentinel的热点规则:

热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。

3.2.6.1 热点规则简单使用
    @RequestMapping("testHotKey")@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKet")public String testHotKey(@RequestParam(value = "p1",required = false) String p1,@RequestParam(value = "p2",required = false) String p2){System.out.println("testHotKey 热点Key--测试");//int age = 10/0;return "testHotKey-------";}/*兜底的自定义方法*/public String deal_testHotKet(String p1, String p2, BlockException e) {return "----deal_testHotKet,------";}

在这里插入图片描述

比如:

localhost:8080/aa?name=aa

localhost:8080/aa?name=b’b

加入两个请求中,带有参数aa的请求访问频次非常高,我们就现在name==aa的请求,但是bb的不限制

如何自定义降级方法,而不是默认的抛出异常?

使用@SentinelResource直接实现降级方法,它等同Hystrix的@HystrixCommand

定义热点规则:

此时我们访问/testHotkey并且带上才是p1

如果qps大于1,就会触发我们定义的降级方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ua0brBgp-1682128319972)(.\图片\sentinel的38.png)]

但是我们的参数是P2,就没有问题

只有带了p1,才可能会触发热点限流
在这里插入图片描述

3.2.6.2 设置热点规则中的其他选项:

在这里插入图片描述

需求:

![[外链图片转存失败,源站可能有防盗链机制,建议在这里插入图片描述

测试
在这里插入图片描述

注意:

参数类型只支持,8种基本类型+String类

注意:

如果我们程序出现异常,是不会走blockHander的降级方法的,因为这个方法只配置了热点规则,没有配置限流规则

我们这里配置的降级方法是sentinel针对热点规则配置的

只有触发热点规则才会降级
在这里插入图片描述

3.2.7 sentinel的系统规则:

系统自适应限流:
从整体维度对应用入口进行限流

对整体限流,比如设置qps到达100,这里限流会限制整个系统不可以

在这里插入图片描述

测试:
在这里插入图片描述

3.2.7.1 @SentinelResource注解:

用于配置降级等功能

@SentinelResource 注解
注意:注解方式埋点不支持 private 方法。

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:
value:资源名称,必需项(不能为空)
entryType:entry 类型,可选项(默认为 EntryType.OUT)
blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
fallback / fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
返回值类型必须与原函数返回值类型一致;
方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
返回值类型必须与原函数返回值类型一致;
方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

1,环境搭建

  1. 为8401添加依赖

    添加我们自己的commone包的依赖

    <dependency><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency>
  1. 额外创建一个controller类RateLimitController
package com.tigerhhzz.springcloud.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import com.tigerhhzz.springcloud.myhandler.Customerhandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author tigerhhzz* @date 2023/4/22 14:50*/
@RestController
public class RateLimitController {@GetMapping("/byResource")@SentinelResource(value = "byResource",blockHandler = "handleException")public CommonResult byResource() {return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));}public CommonResult handleException(BlockException exception) {return new CommonResult(444,exception.getClass().getCanonicalName()+"\t服务不可用");}@GetMapping("/rateLimit/byUrl")@SentinelResource(value = "byUrl")public CommonResult byUrl() {return new CommonResult(200,"按URL限流测试OK",new Payment(2020L,"serial002"));}@GetMapping("/rateLimit/customerBlockHandler")@SentinelResource(value = "customerBlockHandler",blockHandlerClass = Customerhandler.class,blockHandler = "handlerException2")public CommonResult customerBlockHandler() {return new CommonResult(200,"按客户自定义OK",new Payment(2020L,"serial002"));}
}
  1. 配置限流

    注意,我们这里配置规则,资源名指定的是@SentinelResource注解value的值,

    这样也是可以的,也就是不一定要指定访问路径

在这里插入图片描述

  1. 测试.

    可以看到已经进入降级方法了

在这里插入图片描述

  1. 此时我们关闭8401服务

    可以看到,这些定义的规则是临时的,关闭服务,规则就没有了

在这里插入图片描述

可以看到,上面配置的降级方法,又出现Hystrix遇到的问题了

在这里插入图片描述

3.2.7.1.1 自定义限流处理逻辑:
  1. 单独创建一个类Customerhandler ,用于处理限流
package com.tigerhhzz.springcloud.myhandler;import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;/*** @author tigerhhzz* @date 2023/4/22 14:54*/
public class Customerhandler {public CommonResult handlerException(BlockException exception) {return new CommonResult(444,"按客户自定义,global handlerException");}public CommonResult handlerException2(BlockException exception) {return new CommonResult(444,"按客户自定义,global handlerException2");}
}
  1. 在controller中,指定使用自定义类中的方法作为降级方法
    @GetMapping("/rateLimit/customerBlockHandler")@SentinelResource(value = "customerBlockHandler",blockHandlerClass = Customerhandler.class,blockHandler = "handlerException2")public CommonResult customerBlockHandler() {return new CommonResult(200,"按客户自定义OK",new Payment(2020L,"serial002"));}

加 @SentinelResource注解进行服务的限流
用给定的限流名称到sentinel控制台中配置,自定义限流类+方法。
在这里插入图片描述

  1. Sentinel中定义流控规则:

    这里资源名,是以url指定,也可以使用@SentinelResource注解value的值指定

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FBXK2reJ-1682128320028)(.\图片\sentinel的的5.png)]

  2. 测试:

v

3.2.7.2 @SentinelResource注解的其他属性:

在定义了资源点之后,我们可以通过Dashboard来设置限流和降级策略来对资源点进行保护。同时还能
通过@SentinelResource来指定出现异常时的处理策略。
@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。其主要参数如下
在这里插入图片描述

3.2.8 服务熔断:

在这里插入图片描述

3.2.8.1 启动nacos和sentinel
3.2.8.2 新建两个pay模块 9003和9004
  • cloudalibaba-provider-payment9003
  • cloudalibaba-provider-payment9004
  1. pom(两个模块的依赖一样)
<dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></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>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
  1. 配置文件(修改端口号9004)
server:port: 9003spring:cloud:nacos:discovery:server-addr: localhost:8848application:name: nacos-payment-providermanagement:endpoints:web:exposure:include: '*'
  1. 主启动类(修改9004)
package com.tigerhhzz.springcloud;import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;/*** @author tigerhhzz* @date 2023/4/24 9:59*/
@Slf4j
@SpringBootApplication
@EnableDiscoveryClient
public class SentinelPaymentMain9003 {public static void main(String[] args) {SpringApplication.run(SentinelPaymentMain9003.class,args);log.info("SentinelPaymentMain9003启动成功~~~~~~~~~~~~~");}
}
  1. controller(两个模块一样)
package com.tigerhhzz.springcloud.controller;import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;@RestController
public class PaymentController {@Value("${server.post}")private String serverPost;public static HashMap<Long, Payment> hashMap = new HashMap<>();static {hashMap.put(1L,new Payment(1L,"000000000000000001"));hashMap.put(2L,new Payment(2L,"000000000000000002"));hashMap.put(3L,new Payment(3L,"000000000000000003"));}@GetMapping(value = "/paymentSQL/{id}")public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {Payment payment = hashMap.get(id);CommonResult<Payment> result = new CommonResult<>(200,"from mysql,serverPort:"+serverPost);return result;}
}
 **然后启动9003.9004**
3.2.8.3 新建一个order-84消费者模块:

-cloudalibaba-consumer-nacos-order84

  1. pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-tigerhhzz</artifactId><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>cloudalibaba-consumer-nacos-order84</artifactId><dependencies><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><dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></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>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>
  1. 配置文件
server:port: 84spring:application:name: cloud-nacos-ordercloud:nacos:discovery:server-addr: localhost:8848sentinel:transport:dashboard: localhost:8080port: 8719#消费者将要去访问的微服务名称
server-url:nacos-user-service: http://nacos-payment-provider
  1. 主启动类
package com.tigerhhzz.springcloud;import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@Slf4j
@SpringBootApplication
@EnableDiscoveryClient
public class SentinelNacosOrderMain84 {public static void main(String[] args) {SpringApplication.run(SentinelNacosOrderMain84.class,args);log.info("SentinelNacosOrderMain84启动成功~~~~~~~~~~~~~");}
}
  1. 配置类
package com.tigerhhzz.springcloud.config;import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;/*** @author tigerhhzz* @date 2023/4/18 20:31*/
@Configuration
public class ApplicationContextConfig {@Bean@LoadBalancedpublic RestTemplate getRestTemplate() {return new RestTemplate();}
}
  1. controller和service

@SentinelResource注解中

  • fallback管运行异常
  • blockHandler管配置违规

CircleBreakerController类

package com.tigerhhzz.springcloud.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import com.tigerhhzz.springcloud.service.PaymentService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import javax.annotation.Resource;@RestController
public class CircleBreakerController {@Value("${server.post}")private String serverPost;public static final String SERVICE_URL="http://nacos-payment-provider";@Resourceprivate RestTemplate restTemplate;@GetMapping("/consumer/fallback/{id}")
//    @SentinelResource(value = "fallback")  //没有配置
//    @SentinelResource(value = "fallback",fallback = "handlerFallback")  //fallback只负责业务异常
//    @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentine控制台配置违规
//    @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler") //handlerFallback和blockHandler都配置@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler", exceptionsToIgnore = IllegalAccessException.class) //exceptionsToIgnore配置public CommonResult<Payment> fallback(@PathVariable("id") Long id) {CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL+"/paymentSQL/"+id, CommonResult.class,id);if (id==4) {throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");} else if (result.getData() == null) {throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");}return result;}public CommonResult handlerFallback(@PathVariable("id") Long id, Throwable e) {Payment payment = new Payment(id,"null");return new CommonResult(444,"兜底异常handlerFallback,exception内容"+e.getMessage(),payment);}public CommonResult blockHandler(@PathVariable("id") Long id, BlockException exception) {Payment payment = new Payment(id,"null");return new CommonResult(445,"兜底异常handlerFallback,exception内容"+exception.getMessage(),payment);}//--------------Openfeign@Resourceprivate PaymentService paymentService;@GetMapping(value = "/consumer/paymentSQL/{id}")public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {return paymentService.paymentSQL(id);}
}

两个service类

package com.tigerhhzz.springcloud.service;import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class )
public interface PaymentService {@GetMapping(value = "/paymentSQL/{id}")public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
package com.tigerhhzz.springcloud.service;import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import org.springframework.stereotype.Component;@Component
public class PaymentFallbackService implements PaymentService {@Overridepublic CommonResult<Payment> paymentSQL(Long id) {return new CommonResult<>(444,"服务降级返回---PaymentFallbackService",new Payment(id,"errorService"));}
}

为业务方法添加fallback来指定降级方法:
fallback是用于管理异常的,当业务方法发生异常,可以降级到指定方法==
注意,我们这里并没有使用sentinel配置任何规则,但是却降级成功,就是因为
fallback是用于管理异常的,当业务方法发生异常,可以降级到指定方法==

为业务方法添加blockHandler,看看是什么效果
blockHandler只对sentienl定义的规则降级

如果fallback和blockHandler都配置呢?
可以看到,当两个都同时生效时,blockhandler优先生效

@SentinelResource还有一个属性,exceptionsToIgnore
exceptionsToIgnore指定一个异常类,
表示如果当前方法抛出的是指定的异常,不降级,直接对用户抛出异常

3.2.9 sentinel整合ribbon+openFeign+fallback

修改84模块,使其支持feign(注解+接口)

  1. 修改pom
    添加opefeign依赖
        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
  1. 配置文件
#激活sentinel对feign的支持
feign:sentinel:enabled: true
  1. 主启动类,也要修改
package com.tigerhhzz.springcloud;import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;@Slf4j
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class SentinelNacosOrderMain84 {public static void main(String[] args) {SpringApplication.run(SentinelNacosOrderMain84.class,args);log.info("SentinelNacosOrderMain84启动成功~~~~~~~~~~~~~");}
}
  1. 创建远程调用pay模块的接口
package com.tigerhhzz.springcloud.service;import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value = "nacos-payment-provider")
public interface PaymentService {@GetMapping(value = "/paymentSQL/{id}")public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
  1. 创建这个接口的实现类,用于降级
package com.tigerhhzz.springcloud.service;import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import org.springframework.stereotype.Component;@Component
public class PaymentFallbackService implements PaymentService {@Overridepublic CommonResult<Payment> paymentSQL(Long id) {return new CommonResult<>(444,"服务降级返回---PaymentFallbackService",new Payment(id,"errorService"));}
}
  1. 再次修改接口,指定降级类
package com.tigerhhzz.springcloud.service;import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class )
public interface PaymentService {@GetMapping(value = "/paymentSQL/{id}")public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
  1. controller添加远程调用
    //--------------Openfeign@Resourceprivate PaymentService paymentService;@GetMapping(value = "/consumer/paymentSQL/{id}")public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {return paymentService.paymentSQL(id);}
  1. 测试

    启动9003,84
    
  2. 测试,如果关闭9003.看看84会不会降级
    在这里插入图片描述

    **可以看到,正常降级了**
    

熔断框架比较

在这里插入图片描述

3.2.10 sentinel持久化规则

	默认规则是临时存储的,重启sentinel就会消失

在这里插入图片描述

这里以之前的8401为案例进行修改:

  1. 修改8401的pom

    添加:
    <!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
    <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
  2. 修改配置文件:

    添加:

server:port: 8401
spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:#Nacos服务注册中心地址server-addr: localhost:8848sentinel:transport:#配置Sentinel dashboard地址dashboard: localhost:8080port: 8719 #如果8719被占用,自动递增1,直到找到没有被占用的端口#sentinel流控规则用nocas持久化保存配置datasource:ds1:nacos:server-addr: localhost:8848dataId: ${spring.application.name}groupId: DEFAULT_GROUPdata_type: jsonrule_type: flowmanagement:endpoints:web:exposure:include: "*"
 **实际上就是指定,我们的规则要保证在哪个名称空间的哪个分组下**

这里没有指定namespace, 但是是可以指定的

注意,这里的dataid要与8401的服务名一致

  1. 在nacos中创建一个配置文件,dataId就是上面配置文件中指定的
[{// 资源名"resource": "/rateLimit/byUrl",// 针对来源,若为 default 则不区分调用来源"limitApp": "default",// 限流阈值类型(1:QPS;0:并发线程数)"grade": 1,// 阈值"count": 1,// 是否是集群模式"clusterMode": false,// 流控效果(0:快速失败;1:Warm Up(预热模式);2:排队等待)"controlBehavior": 0,// 流控模式(0:直接;1:关联;2:链路)"strategy": 0// 预热时间(秒,预热模式需要此参数)//"warmUpPeriodSec": 10,// 超时时间(排队等待模式需要此参数)//"maxQueueingTimeMs": 500,// 关联资源、入口资源(关联、链路模式)//"refResource": "rrr"}
]

在这里插入图片描述
在这里插入图片描述

注意:配置到nacos中时,把注释都去掉,有注释的json配置,规则不保存。

  1. 关闭8401,然后重启8401,此时需要需要首先访问一下接口地址http://localhost:8401/rateLimit/byUrl
    然后再次刷新sentinel,又可以正常读取到规则,那么证明持久化成功

4、SpringCloud Alibaba Seata处理分布式事务

4.1 什么是分布式事务

一次业务操作需要跨多个数据源或需要多个系统进行远程调用,就会产生分布式事务问题
在这里插入图片描述
在这里插入图片描述

4.2 分布式事务中的一些概念

分布式事务中的一些概念,也是seata中的概念:

seata官网地址:http://seata.io/zh-cn/

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

一个经典的分布式事务处理过程=1+3
1个全局唯一的事务ID
3个组件

在这里插入图片描述

  • TC (Transaction Coordinator) - 事务协调者 维护全局和分支事务的状态,驱动全局事务提交或回滚。

  • TM (Transaction Manager) - 事务管理器 定义全局事务的范围:开始全局事务、提交或回滚全局事务。

  • RM (Resource Manager) - 资源管理器
    管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

在这里插入图片描述

4.3 seata安装和启动:

  1. 下载安装seata的安装包

  2. 修改bin下面的file.conf和registry.conf

在这里插入图片描述
在这里插入图片描述

alibaba seata分布式事务中bin/file.conf和registry.conf 修改后的文件下载地址:

https://mp.csdn.net/mp_download/manage/download/UpDetailed

  1. mysql建库建表

    • 上面指定了数据库为seata,所以创建一个数据库名为seata

    • 建表,在seata的安装目录下有一个db_store.sql,运行即可

  2. 继续修改配置文件,修改registry.conf

配置seata作为微服务,指定注册中心

  1. 启动

先启动nacos

在启动seata-server(运行安装目录下的,seata-server.bat)

4.3 商品交易案例

4.3.1 业务说明

在这里插入图片描述

下单—>库存—>账号余额

4.3.2 创建三个数据库

CREATE DATABASE seata_order;CREATE DATABASE seata_storage;CREATE DATABASE seata_account;

在这里插入图片描述

4.3.3 创建对应的表以及创建回滚日志表,方便查看

CREATE TABLE `t_account` (`id` bigint NOT NULL COMMENT 'id',`user_id` bigint DEFAULT NULL COMMENT '用户id',`total` decimal(10,0) DEFAULT NULL COMMENT '总额度',`used` decimal(10,0) DEFAULT NULL COMMENT '已用余额',`residue` decimal(10,0) DEFAULT NULL COMMENT '剩余可用额度',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='账户表';CREATE TABLE `undo_log` (`id` bigint NOT NULL AUTO_INCREMENT,`branch_id` bigint NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int 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 DEFAULT CHARSET=utf8;CREATE TABLE `t_order` (`int` bigint NOT NULL AUTO_INCREMENT,`user_id` bigint DEFAULT NULL COMMENT '用户id',`product_id` bigint DEFAULT NULL COMMENT '产品id',`count` int DEFAULT NULL COMMENT '数量',`money` decimal(11,0) DEFAULT NULL COMMENT '金额',`status` int DEFAULT NULL COMMENT '订单状态:  0:创建中 1:已完结',PRIMARY KEY (`int`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='订单表';CREATE TABLE `undo_log` (`id` bigint NOT NULL AUTO_INCREMENT,`branch_id` bigint NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int 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 DEFAULT CHARSET=utf8;CREATE TABLE `t_storage` (`int` bigint NOT NULL AUTO_INCREMENT,`product_id` bigint DEFAULT NULL COMMENT '产品id',`total` int DEFAULT NULL COMMENT '总库存',`used` int DEFAULT NULL COMMENT '已用库存',`residue` int DEFAULT NULL COMMENT '剩余库存',PRIMARY KEY (`int`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='库存';CREATE TABLE `undo_log` (`id` bigint NOT NULL AUTO_INCREMENT,`branch_id` bigint NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int 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 DEFAULT CHARSET=utf8;

注意每个库都要执行一次这个sql,生成回滚日志表

4.3.4 创建微服务模块

业务需求:下订单----减库存—扣余额—改(订单)状态

每个业务都创建一个微服务,也就是要有三个微服务,订单,库存,账号

  • 订单模块,seata-order-service2001

  • 库存模块, seata-storage-service2002

  • 账号模块,seata-account-service2003

4.3.4.1 订单模块seata-order-service2001
  1. 修改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-tigerhhzz</artifactId><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>seata-order-service2001</artifactId><dependencies><!--        包含了Sleuth--><!--        <dependency>--><!--            <groupId>org.springframework.cloud</groupId>--><!--            <artifactId>spring-cloud-starter-zipkin</artifactId>--><!--        </dependency>--><!-- Nacos--><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><exclusions><exclusion><artifactId>seata-all</artifactId><groupId>io.seata</groupId></exclusion></exclusions></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version>1.0.0</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></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>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>
  1. 配置文件yml

    server:port: 2001spring:application:name: seata-order-servicecloud:alibaba:seata:# 自定义事务组名称需要与seata-server中的对应,我们之前在seata的配置文件中配置的名字tx-service-group: fsp_tx_groupnacos:discovery:server-addr: 127.0.0.1:8848datasource:# 当前数据源操作类型type: com.alibaba.druid.pool.DruidDataSource# mysql驱动类driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8username: rootpassword: root
    feign:hystrix:enabled: false
    logging:level:io:seata: infomybatis:mapperLocations: classpath*:mapper/*.xml
  2. 配置文件file.conf和registry.conf

     - 创建配置文件file.conf
    

在这里插入图片描述

	- 创建registry.conf:

在这里插入图片描述

两个配置文件的下载地址:https://download.csdn.net/download/weixin_43025151/87722693

==实际上,就是要将seata中的我们之前修改的两个配置文件复制到这个项目下==
  1. domain

两个domain

  • CommonResult
  • Order
package com.tigerhhzz.springcloud.domain;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author tigerhhzz* @date 2023/4/24 10:58*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {private Integer code;private String message;private T data;public CommonResult(Integer code, String message) {this(code, message, null);}
}
package com.tigerhhzz.springcloud.domain;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;/*** @author tigerhhzz* @date 2023/4/24 10:56*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {private Long id;private Long user_id;private Long product_id;private Integer count;private BigDecimal money;private Integer status; //订单状态:0:创建中;1:已完结
}
  1. Dao接口及实现

两个方法=下订单+改(订单)状态

package com.tigerhhzz.springcloud.dao;import com.tigerhhzz.springcloud.domain.Order;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;/*** @author tigerhhzz* @date 2023/4/24 10:54*/
@Mapper
public interface OrderDao {//创建订单void create(Order order);//修改订单状态void update(@Param("userId") Long userId, @Param("status") Integer status);
}
  1. Service接口及实现

OrderMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.springcloud.alibaba.dao.OrderDao"><resultMap id="order" type="com.tigerhhzz.springcloud.domain.Order"><result property="id" column="id" jdbcType="BIGINT"/><result property="user_id" column="userId" jdbcType="BIGINT"/><result property="product_id" column="productId" jdbcType="BIGINT"/><result property="count" column="count" jdbcType="INTEGER"/><result property="money" column="money" jdbcType="BIGINT"/><result property="status" column="status" jdbcType="INTEGER"/></resultMap><insert id="create">insert into t_order(user_id,product_id,count,money,status)value (#{userId},#{productId},#{count},#{money},0)</insert><update id="update">update t_order set status = 1where user_id = #{userId} and status = #{status}</update>
</mapper>

OrderService

package com.tigerhhzz.springcloud.service;import com.tigerhhzz.springcloud.domain.Order;/*** @author tigerhhzz* @date 2023/4/24 11:01*/
public interface OrderService {void create(Order order);
}

AccountService

package com.tigerhhzz.springcloud.service;import com.tigerhhzz.springcloud.domain.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;import java.math.BigDecimal;/*** @author tigerhhzz* @date 2023/4/24 11:02*/
@Component
@FeignClient(value = "seata-account-service")
public interface AccountService {@PostMapping("/account/decrease")CommonResult decrease(@RequestParam("userId")Long userId,@RequestParam("money") BigDecimal money);
}

StorageService

package com.tigerhhzz.springcloud.service;import com.tigerhhzz.springcloud.domain.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;/*** @author tigerhhzz* @date 2023/4/24 11:03*/
@Component
@FeignClient(value = "seata-storage-service")
public interface StorageService {@PostMapping(value = "/storage/decrease")CommonResult decrease(@RequestParam("productId") Long productId,@RequestParam("count") Integer count);
}

OrderServiceImpl

package com.tigerhhzz.springcloud.service.impl;import com.tigerhhzz.springcloud.dao.OrderDao;
import com.tigerhhzz.springcloud.domain.Order;
import com.tigerhhzz.springcloud.service.AccountService;
import com.tigerhhzz.springcloud.service.OrderService;
import com.tigerhhzz.springcloud.service.StorageService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** @author tigerhhzz* @date 2023/4/24 11:04*/
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {@Resourceprivate OrderDao orderDao;@Resourceprivate StorageService storageService;@Resourceprivate AccountService accountService;@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)@Overridepublic void create(Order order) {log.info("-------->开始创建新订单");orderDao.create(order);log.info("--------订单微服务开始调用库存,做扣减");storageService.decrease(order.getProduct_id(),order.getCount());log.info("-------订单微服务开始调用库存,做扣减end");log.info("-------订单微服务开始调用账户,做扣减");accountService.decrease(order.getUser_id(),order.getMoney());log.info("-------订单微服务开始调用账户,做扣减end");log.info("-------修改订单状态");orderDao.update(order.getUser_id(),0);log.info("-------修改订单状态结束");log.info("--------下订单结束了,哈哈哈哈");}
}
  1. Controller

OrderController

package com.tigerhhzz.springcloud.controller;import com.tigerhhzz.springcloud.domain.CommonResult;
import com.tigerhhzz.springcloud.domain.Order;
import com.tigerhhzz.springcloud.service.OrderService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @author tigerhhzz* @date 2023/4/24 11:07*/
@RestController
public class OrderController {@Resourceprivate OrderService orderService;@GetMapping("/order/create")public CommonResult create(Order order) {orderService.create(order);return new CommonResult(200,"订单创建成功!");}
}
  1. Config配置
  • MybatisConfig
  • DataSourceProxyConfig
package com.tigerhhzz.springcloud.config;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;/*** @author tigerhhzz* @date 2023/4/24 11:08*/
@Configuration
@MapperScan({"com.tigerhhzz.springcloud.dao"})
public class MybatisConfig {
}
package com.tigerhhzz.springcloud.config;import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import javax.sql.DataSource;/*** @author tigerhhzz* @date 2023/4/24 11:09*/
@Configuration
public class DataSourceProxyConfig {//    @Value("${mybatis.mapperLocations}")
//    private String mapperLocations;@Bean@ConfigurationProperties(prefix = "spring.datasource")public DataSource druidDataSource() {return new DruidDataSource();}@Beanpublic DataSourceProxy dataSourceProxy(DataSource dataSource) {return new DataSourceProxy(dataSource);}@Beanpublic SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSourceProxy);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
//        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}
}
  1. 主启动类

SeataOrderMainApp2001

package com.tigerhhzz.springcloud;import lombok.extern.slf4j.Slf4j;
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;/*** @author tigerhhzz* @date 2023/4/24 10:48*/
@Slf4j
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源的自动创建
@EnableFeignClients
@EnableDiscoveryClient
public class SeataOrderMainApp2001 {public static void main(String[] args) {SpringApplication.run(SeataOrderMainApp2001.class,args);log.info("SeataOrderMainApp2001启动成功~~~~~~~~~~~~~");}
}

启动SeataOrderMainApp2001服务;

先启动nacos,再启动seata服务,等seata服务注册进nacos后,最后启动SeataOrderMainApp2001服务;
启动成功,服务注册进nacos;
在这里插入图片描述

4.3.4.2 库存模块seata-storage-service2002
  1. 修改pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud-tigerhhzz</artifactId><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>seata-storage-service2002</artifactId><dependencies><!--        包含了Sleuth--><!--        <dependency>--><!--            <groupId>org.springframework.cloud</groupId>--><!--            <artifactId>spring-cloud-starter-zipkin</artifactId>--><!--        </dependency>--><!-- Nacos--><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><exclusions><exclusion><artifactId>seata-all</artifactId><groupId>io.seata</groupId></exclusion></exclusions></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version>0.9.0</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></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>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.tigerhhzz.springcloud-tigerhhzz</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies></project>
  1. 配置文件
server:port: 2002spring:application:name: seata-storage-servicecloud:alibaba:seata:tx-service-group: fsp_tx_groupnacos:discovery:server-addr: localhost:8848datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: org.gjt.mm.mysql.Driverurl: jdbc:mysql://localhost:3306/seata_storage?useUnicode=true&characterEncoding=utf-8&useSSL=falseusername: rootpassword: 123456feign:hystrix:enabled: falselogging:level:io:seata: infomybatis:mapper-locations: classpath:mapper/*.xml
  1. 配置文件file.conf和registry.conf

同订单模块一样

  1. Domain

两个domain

  • Storage
  • CommonResult (同订单模块一样)
package com.tigerhhzz.springcloud.domain;import lombok.Data;/*** @author tigerhhzz* @date 2023/4/24 11:26*/
@Data
public class Storage {private Long id;/*** 产品id*/private Long product_id;/*** 总库存*/private Integer total;/*** 已用库存*/private Integer used;/*** 剩余库存*/private Integer residue;
}
  1. Dao层

StorageDao

package com.tigerhhzz.springcloud.dao;import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;/*** @author tigerhhzz* @date 2023/4/24 11:29*/
@Mapper
public interface StorageDao {void decrease(@Param("product_id") Long productId, @Param("count") Integer count);
}

StorageMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tigerhhzz.springcloud.dao.StorageDao"><resultMap id="order" type="com.tigerhhzz.springcloud.domain.Storage"><result property="id" column="id" jdbcType="BIGINT"/><result property="product_id" column="productId" jdbcType="BIGINT"/><result property="total" column="total" jdbcType="INTEGER"/><result property="used" column="used" jdbcType="INTEGER"/><result property="residue" column="residue" jdbcType="INTEGER"/></resultMap><update id="decrease">update t_storage set used = used + #{count},residue = residue -#{count}where product_id = #{productId}</update>
</mapper>
  1. service层

StorageService

package com.tigerhhzz.springcloud.service;public interface StorageService {/*** 扣减库存* @param productId* @param count*/void decrease(Long productId,Integer count);
}

StorageServiceimpl


import com.tigerhhzz.springcloud.dao.StorageDao;
import com.tigerhhzz.springcloud.service.StorageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
@Slf4j
public class StorageServiceimpl implements StorageService {@Resourceprivate StorageDao storageDao;@Overridepublic void decrease(Long productId, Integer count) {log.info("库存扣减开始----");storageDao.decrease(productId,count);log.info("库存扣减结束----");}
}
  1. controller层
package com.tigerhhzz.springcloud.controller;import com.tigerhhzz.springcloud.domain.CommonResult;
import com.tigerhhzz.springcloud.service.StorageService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
@RequestMapping(value = "/storage")
public class StorageController {@Resourceprivate StorageService storageService;@PostMapping(value = "/decrease")public CommonResult decrease(@RequestParam("productId") Long productId,@RequestParam("count") Integer count) {storageService.decrease(productId, count);return new CommonResult(200,"库存扣减成功,哈哈哈哈");}
}
  1. 两个config(同订单模块一样)
  • MybatisConfig
  • DataSourceProxyConfig
  1. 主启动类
package com.tigerhhzz.springcloud;import lombok.extern.slf4j.Slf4j;
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;/*** @author tigerhhzz* @date 2023/4/24 11:28*/
@Slf4j
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients
@EnableDiscoveryClient
public class SeataStorageMainApp2002 {public static void main(String[] args) {SpringApplication.run(SeataStorageMainApp2002.class,args);log.info("SeataStorageMainApp2002启动成功~~~~~~~~~~~~~");}
}
4.3.4.3 账号模块seata-account-service2003
  1. 修改pom

依赖同库存模块一样

  1. 配置文件yml

端口号和服务名使用自己的,其他配置同库存模块一样

  1. 配置文件file.conf和registry.conf

同订单模块一样

  1. Domain

两个domain

  • Acount
  • CommonResult (同订单模块一样)
package com.tigerhhzz.springcloud.domain;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;/*** @author tigerhhzz* @date 2023/4/24 10:56*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Acount {private Long id;/*** 用户id*/private Long user_id;/*** 总额度*/private BigDecimal total;/*** 已用额度*/private BigDecimal used;/*** 剩余额度*/private BigDecimal residue;}
  1. Dao层

AccountDao


package com.tigerhhzz.springcloud.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") Long userId,@Param("money") BigDecimal money);
}

AccountMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tigerhhzz.springcloud.dao.AccountDao"><resultMap id="account" type="com.tigerhhzz.springcloud.domain.Account"><result property="id" column="id" jdbcType="BIGINT"/><result property="user_id" column="userId" jdbcType="BIGINT"/><result property="total" column="total" jdbcType="DECIMAL"/><result property="used" column="used" jdbcType="DECIMAL"/><result property="residue" column="residue" jdbcType="DECIMAL"/></resultMap><update id="decrease">update t_account set used = used + #{money},residue = residue - #{money}where user_id=#{userId}</update>
</mapper>
  1. service层

AccountService

package com.tigerhhzz.springcloud.service;import java.math.BigDecimal;public interface AccountService {void decrease(Long userId, BigDecimal money);
}

AccountServiceimpl

package com.tigerhhzz.springcloud.service.impl;import com.tigerhhzz.springcloud.dao.AccountDao;
import com.tigerhhzz.springcloud.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.math.BigDecimal;@Service
@Slf4j
public class AccountServiceImpl implements AccountService {@Resourceprivate AccountDao accountDao;@Overridepublic void decrease(Long userId, BigDecimal money) {log.info("账户扣除余额开始---");accountDao.decrease(userId, money);log.info("账户扣除余额结束---");}
}
  1. controller层
package com.tigerhhzz.springcloud.controller;import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.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;@PostMapping(value = "decrease")public CommonResult decrease(@RequestParam("userId") Long userId,@RequestParam("money")BigDecimal money) {accountService.decrease(userId, money);return new CommonResult(200,"账户余额扣减成功,哈哈哈");}
}
  1. 两个config(同订单模块一样)
  • MybatisConfig
  • DataSourceProxyConfig
  1. 主启动类

SeataAccountMainApp2003

package com.tigerhhzz.springcloud;import lombok.extern.slf4j.Slf4j;
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;@Slf4j
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients
@EnableDiscoveryClient
public class SeataAccountMainApp2003 {public static void main(String[] args) {SpringApplication.run(SeataAccountMainApp2003.class,args);log.info("SeataAccountMainApp2003启动成功~~~~~~~~~~~~~");}
}
4.3.4.4 数据库初始情况

数据库seata_account

表t_account

在这里插入图片描述

数据库seata_storage

表t_storage

在这里插入图片描述

4.3.4.5 正常下单

启动2001 访问地址:http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100

在这里插入图片描述

订单库中增加一条记录

在这里插入图片描述

库存库中的记录

在这里插入图片描述

账户库中的记录

在这里插入图片描述

4.3.4.6 超时异常,没加@GlobalTransactional

2003模块AccountServiceImpl

        //模拟超时异常,全局事务回滚try {TimeUnit.SECONDS.sleep(20);}catch (InterruptedException e){e.printStackTrace();}

重启2003
访问地址:http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100

在这里插入图片描述

检查数据库
订单库中增加了数据,但是订单状态为0,代表未支付

在这里插入图片描述

库存表中扣除了

在这里插入图片描述

账号表中也被扣钱了

在这里插入图片描述

故障情况:
当库存和账号金额扣减后,订单状态并没有设置为已完成,没有从0改为1
而且由于feign的重试机制,账号余额还有可能被多次扣减

4.3.4.7 超时异常,添加@GlobalTransactional

修改2001模块OrderServiceImpl 添加注解

@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)

重启2001,访问地址http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100

当出现异常时,三个数据库中并没有增加记录。
发生异常后,直接回滚了,前面的修改操作都回滚了。

4.4 setat原理

在这里插入图片描述
seata-server-1.4.2

在这里插入图片描述
在这里插入图片描述

seata提供了四个模式:

第一阶段:

在这里插入图片描述

二阶段之提交:

在这里插入图片描述

二阶段之回滚:

在这里插入图片描述

断点:

在这里插入图片描述

可以看到,他们的xid全局事务id是一样的,证明他们在一个事务下

在这里插入图片描述

before 和 after的原理就是

在这里插入图片描述

在更新数据之前,先解析这个更新sql,然后查询要更新的数据,进行保存

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_103356.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

适合学生党的蓝牙耳机品牌有哪些?性价比高的无线耳机推荐

相较于有线耳机&#xff0c;蓝牙耳机的受欢迎程度可谓是越来越高&#xff0c;当然&#xff0c;这也离不开部分手机取消耳机孔的设计。最近看到很多网友问&#xff0c;适合学生党的蓝牙耳机品牌有哪些&#xff1f;针对这个问题&#xff0c;我来给大家推荐几款性价比高的无线耳机…

static_cast、dynamic_cast和reinterpret_cast区别和联系

其实网上相关的资料不少&#xff0c;但是能够说清楚明白这个问题的也不多。 于是&#xff0c;我尝试着问了一下AI&#xff0c;感觉回答还可以&#xff0c;但是需要更多的资料验证。 让我们先看看AI是怎么回答这个问题的。 static_cast、dynamic_cast和reinterpret_cast都是C中…

视频音频提取器推荐:快速提取视频中的音频!

视频中的音频可以用于很多用途&#xff0c;比如制作配乐、音频剪辑等。但是&#xff0c;许多人并不知道如何将视频中的音频提取出来。如果您也是这样的情况&#xff0c;那么本文为您介绍一个简单易用的视频音频提取器&#xff1a;。 它是一个免费的在线工具&#xff0c;可以帮…

如何在Web上实现激光点云数据在线浏览和展示?

无人机激光雷达测量是一项综合性较强的应用系统&#xff0c;具有数据精度高、层次细节丰富、全天候作业等优势&#xff0c;能够精确测量三维现实世界&#xff0c;为各个行业提供了丰富有效的数据信息。但无人机激光雷达测量产生的点云数据需要占用大量的存储空间&#xff0c;甚…

DataGridView 真·列头不高亮 真·列头合并

高亮BUG VB.Net&#xff0c;在 .NET Framework 4.8 的 WinForm 下(即不是 WPF 的绘图模式、也不是 Core 或 Mono 的开发框架)&#xff0c;使用 DataGridView 行模式&#xff0c;还是有个列头表现为高亮显示&#xff1a; 查找各种解决方式: 设置 ColumnHeadersDefaultCellSty…

YOLOv1代码复现2:数据加载器构建

YOLOv1代码复现2&#xff1a;数据加载器构建 前言 ​ 在经历了Faster-RCNN代码解读的摧残后&#xff0c;下决心要搞点简单的&#xff0c;于是便有了本系列的博客。如果你苦于没有博客详细告诉你如何自己去实现YOLOv1&#xff0c;那么可以看看本系列的博客&#xff0c;也许可以帮…

【Java实战篇】Day13.在线教育网课平台--生成支付二维码与完成支付

文章目录 一、需求&#xff1a;生成支付二维码1、需求分析2、表设计3、接口定义4、接口实现5、完善controller 二、需求&#xff1a;查询支付结果1、需求分析2、表设计与模型类3、接口定义4、接口实现步骤一&#xff1a;查询支付结果步骤二&#xff1a;保存支付结果&#xff08…

VUE3如何定义less全局变量

默认已经安装好了less&#xff0c;这里不过多讲。 &#xff08;1&#xff09;首先我们需要下载一个插件依赖&#xff1a; npm i style-resources-loader --save-dev &#xff08;2&#xff09;VUE3里配置vue.config.js文件内容 代码&#xff1a; const path require("p…

HashMap如何解决哈希冲突

HashMap如何解决哈希冲突 Hash算法和Hash表Hash冲突解决哈希冲突的方法开放地址法链式寻址法再hash法建立公共溢出区 Hash算法和Hash表 Hash算法就是把任意长度的输入通过散列算法编程固定长度的输出。这个输出结果就是一个散列值。 Hash表又称为“散列表”&#xff0c;它是通…

SpringBoot中一个注解优雅实现重试Retry框架

目录: 1、简介2、实现步骤 1、简介 重试&#xff0c;在项目需求中是非常常见的&#xff0c;例如遇到网络波动等&#xff0c;要求某个接口或者是方法可以最多/最少调用几次&#xff1b;实现重试机制&#xff0c;非得用Retry这个重试框架吗&#xff1f;那肯定不是&#xff0c;相信…

Mysql 查询同类数据中某一数字最大的所有数据

方法一、将时间进行排序后再分组 该表表名为customer, park_id表示园区id&#xff0c;joined_at表示用户的加入时间&#xff0c;created_at表示用户的创建时间。 需求&#xff1a;查出每个园区中&#xff0c;最早加入园区的第一位用户 select * from (select * from custome…

数据库课设--基于Python+MySQL的餐厅点餐系统(表的设计)

文章目录 一、系统需求分析二、系统设计1. 功能结构设计2、概念设计2.2.1 bill_food表E-R图2.2.2 bills表E-R图2.2.3 categories E-R图2.2.4 discounts表 E-R图2.2.5 emp表E-R图2.2.6 food 表E-R图2.2.7 member表E-R图2.2.8 member_point_bill表E-R图2.2.9 servers表E-R图2.2.1…

操作系统考试复习—第二章 2.1 2.2程序和进程的描述

第二章 进程的描述与控制 程序&#xff1a;有序的指令集合 程序顺序执行的特征&#xff1a;1.顺序性 2.封闭性 3.可再现性(确定性) 在多道程序环境下&#xff0c;允许多个程序并发执行&#xff0c;此时他们将失去封闭性&#xff0c;并具有间断性和不可再现性的特征。为此引…

基于SGM431的电路设计问题分析

本案例中,采用SGM431芯片设计了一个过压保护电路。 这个电路初次设计,有很多的问题,下面逐一分析 1.当输入24V,测得Vref=1.59V。Vout为1.15V;,mos管关断 2。经过多次测量发现,临界值在10V到10.5之间; 当输入10.5V时,测量Vref=1.69V。vout=1.15V;mos管关断 当输入1…

智慧物联网边缘协同感知(EICS)技术方案: 低功耗无线扫描唤醒技术

物联网的传感器或控制节点通常有体积限制&#xff0c;只能使用钮扣电池、小型电池&#xff0c;甚至使用能量收集源进行运作。在许多工业应用中&#xff0c;需要人工更换电池的成本&#xff0c;特别是在难以接近地方更换所需的成本&#xff0c;使得人们更加重视降低平均电流消耗…

深度学习入门到实践:相关基础概述

绪论 深度学习&#xff08;Deep Learning&#xff09;是近年来发展十分迅速的研究领域&#xff0c;并且在人工智能的很多子领域都取得了巨大的成功。从根源来讲&#xff0c;深度学习是机器学习的一个分支&#xff0c;是指一类问题以及解决这类问题的方法。     深度学习问题…

halcon灰度积分投影/垂直积分投影

简介:关于灰度投影积分可以用到的场合很多,例如分割字符,分割尺子上的刻度等,适用于有规律的变化这些内容的检测。本文复现了论文《基于深度学习和灰度纹理特征的铁路接触网绝缘子状态检测》中灰度积分投影实现了对绝缘子缺陷位置的检测。见(图1)灰度积分垂直方向投影获得…

AI智能智能课程第四讲 -数据库领域专家

使用chatGPT让你成为数据库领域专家 作业 现在要测试电商的下单功能&#xff1a;测试员张三在公司的电商平台上下了几个单&#xff0c;现在需要验证&#xff1a;张三这个客户下单的所有订单信息&#xff0c;包含订单编号&#xff0c;商品名称&#xff0c;商品价格&#xff0c;…

分支和循环语句(2)

文章目录 3.2 for循环3.2.1 for语句的语法3.2.2 for循环中的break和continue3.2.3 for语句的循环控制变量3.2.4 一些for循环的变种3.2.5 一道笔试题 3.3 do while循环3.3.1 do语句的语法3.3.2 do语句的特点3.3.3 do while循环中的break和continue 3.4 练习3.4.1 计算 n的阶乘3.…

Compiler- 尾调用

我们还是用例子来引入本次要探讨的问题--尾调用 #include <stdio.h>int fib(int a) {return a < 2 ? 1 : fib(a - 1) fib(a - 2); }int main() {int n,result;scanf("%d",&n);result fib(n);printf("result is %d.\n",result);return 0; …