SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)

重点 : 约定 > 配置 > 编码

降级方法的参数跟原方法的原始值相同

编码五部曲:

  1. 建module
  2. 改pom
  3. 写yml
  4. 主启动
  5. 业务类

好的环境配置比代码更重要

所有源码地址:https://gitee.com/xyy-kk_admin/spring-cloud

入门篇链接:https://blog.csdn.net/qiwunongqingyin/article/details/117927188
初级篇链接:https://blog.csdn.net/qiwunongqingyin/article/details/118028552
中级篇链接:https://blog.csdn.net/qiwunongqingyin/article/details/118151377
高级篇链接:https://blog.csdn.net/qiwunongqingyin/article/details/118411828


  1. SpringCloud Alibaba 入门简介
  2. SpringCloud Alibaba Nacos 服务注册和配置中心
  3. SpringCloud Alibaba Sentinel 现实熔断与限流
  4. SpringCloud Alibaba Seata处理分布式事务

17. SpringCloud Alibaba 入门简介

官网: https://spring.io/projects/spring-cloud-alibaba

官方参考手册:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html

中文文档 : https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md

Spring Cloud Dalston、Edgware、Finchley 和 Greenwich 都已达到生命周期终止状态,不再受支持。
在这里插入图片描述

  1. 什么是维护模式?

    将模块置于维护模式,意味着Spring Cloud团队将不会再向模块添加新功能。

    他们将修复block级别的 bug 以及安全问题,他们也会考虑并审查社区的小型pull request。

  2. 微服务关系:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第1张图片

17.1 SpringCloud Alibaba 带来了什么?

服务限流降级:默认支持Servlet、Feign、RestTemplate、Dubbo和RocketMQ限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级Metrics 监控。

服务注册与发现:适配Spring Cloud服务注册与发现标准,默认集成了Ribbon的支持分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。

消息驱动能力:基于Spring Cloud Stream为微服务应用构建消息驱动能力。

阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于Cron表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有Worker (schedulerx-client)上执行。

18. SpringCloud Alibaba Nacos 服务注册和配置中心

官网: https://spring.io/projects/spring-cloud-alibaba#learn

中文网: https://nacos.io/zh-cn/

下载地址: https://github.com/alibaba/nacos/tags 自己寻找版本下载,(我自己2.0.2)

18.1 概述

为什么叫 Nacas?

  • 前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service。

是什么?

  • 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
  • Nacos: Dynamic Naming and Configuration Service
  • Nacos就是注册中心+配置中心的组合 -> Nacos = Eureka+Config+Bus

能干嘛?

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

框架对比

服务注册与发现框架 CAP模型 控制台管理 社区活跃度
Eureka AP 支持 低(2.x版本闭源)
Zookeeper CP 不支持
consul CP 支持
Nacos AP 支持

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

18.2 下载与安装

  1. 本地 Java8 与 maven 环境 ok !!

  2. 下载地址: https://github.com/alibaba/nacos/tags 自己寻找版本下载,(我自己2.0.2,目前推荐的最稳定的版本)

  3. 解压进入bin目录,启动startup.cmd(直接启动是集群环境会报错…)
    打开cmd命令窗口,输入: startup.cmd -m standalone 启动单机模式
    注: wdnmd(唯独你没懂),第一次加载时间超长,可以去吃个饭再回来…

  4. cmd命令窗口会显示访问路径的: http://localhost:8848/nacos
    账号和密码默认都是nacos
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第2张图片

18.3 基于 Nacos 的服务提供者

spring-cloud alibaba 官网都有的

  1. 新建子项目cloudalibaba-provider-payment9001

  2. pom
    父pom:(应该已经有了)

      
      <dependency>
          <groupId>com.alibaba.cloudgroupId>
          <artifactId>spring-cloud-alibaba-dependenciesartifactId>
          <version>2.2.0.RELEASEversion>
          <type>pomtype>
          <scope>importscope>
      dependency>
    

    9001pom:

    
    <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>cloudartifactId>
            <groupId>com.xyygroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloudalibaba-provider-payment9001artifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
        dependencies>
    project>
    
  3. yml

    server:
      port: 9001
    spring:
      application:
        name: nacos-payment-provider
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
  4. 启动类PaymentMain9001,注解:@EnableDiscoveryClient

  5. 业务controller:PaymentNacosController

    @RestController
    @RequestMapping("payment/nacos")
    public class PaymentNacosController {
        @Value("${server.port}")
        private String serverPort;
    
        @GetMapping(value = "getport/{id}")
        public String getPayment(@PathVariable("id") Integer id) {
            return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
        }
    }
    
  6. 访问测试: http://localhost:9001/payment/nacos/getport/1
    nacos页面: http://localhost:8848/nacos -> 服务管理 -> 服务列表 可以看到已经注册成功
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第3张图片

  7. 为了演示集群,根据9001 新建cloudalibaba-provider-payment9002
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第4张图片
    或者不新建,使用模拟的方式copy9001:

    1. 右击server栏的9001,复制配置
    2. 在VM options(VM选项)一栏中填入 -> -DServer.port=9002 没空格有点,我没写错
    3. 确认即可

18.4 基于 Nacos 的服务消费者

nacos内嵌ribbon:
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第5张图片

  1. 新建cloudalibaba-consumer-nacos-order80
  2. pom
    	
    <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>cloudartifactId>
            <groupId>com.xyygroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloudalibaba-consumer-nacos-order80artifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
    
            <dependency>
                <groupId>com.xyygroupId>
                <artifactId>cloud-api-commonsartifactId>
                <version>${project.version}version>
            dependency>
        dependencies>
    
    project>
    
  3. yml
    server:
      port: 80
    
    spring:
      application:
        name: nacos-order-consumer
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
            
    # 可写可不写,Controller要用,提出来 类 也一样
    #消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
    service-url:
      nacos-user-service: http://nacos-payment-provider
    
  4. 启动类OrderNacosMain80,注解:@EnableDiscoveryClient
  5. 配置类:ApplicationContextConfig
    @Configuration
    public class ApplicationContextConfig
    {
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate(){
            return new RestTemplate();
        }
    }
    
  6. 业务类controller:OrderNacosController
    @RestController
    @RequestMapping("order/nacos")
    public class OrderNacosController {
    
        @Resource
        private RestTemplate restTemplate;
    	//这个就是在配置类中写的路径
        @Value("${service-url.nacos-user-service}")
        private String serverURL;
    
        @GetMapping(value = "getport/{id}")
        public String getPayment(@PathVariable("id") Integer id) {
            return restTemplate.getForObject(serverURL+"/payment/nacos/getport/"+id,String.class);
        }
    }
    
  7. 启动测试: http://localhost/order/nacos/getport/1
    nacos:在这里插入图片描述

18.5 Nacos服务注册中心对比提升

  1. Nacos全景图:

    Nacos和CAP

  2. Nacos与其他注册中心特性对比
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第6张图片

  3. Nacos服务发现实例模型
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第7张图片

  4. Nacos支持AP和CP模式的切换

    C是所有节点在同一时间看到的数据是一致的;而A的定义是所有的请求都会收到响应。

    何时选择使用何种模式?

    —般来说,如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如Spring cloud和Dubbo服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。

    如果需要在服务级别编辑或者存储配置信息,那么CP是必须,K8S服务和DNS服务则适用于CP模式。CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。

    切换命令:
    curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP

18.6 Nacos 配置中心 -> 基础配置

  1. 新建子项目cloudalibaba-config-nacos-client3377

  2. pom

    
    <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>cloudartifactId>
            <groupId>com.xyygroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloudalibaba-config-nacos-client3377artifactId>
    
        <dependencies>
    
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
            dependency>
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
        dependencies>
    project>
    
  3. yml
    配置规则: https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
    bootstrap.yml是云配置优先,application.yml是本地

    bootstrap.yml:

    # nacos配置
    server:
      port: 3377
    
    spring:
      application:
        name: nacos-config-client
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 #Nacos服务注册中心地址
          config:
            server-addr: localhost:8848 #Nacos作为配置中心地址
            file-extension: yaml #指定yaml格式的配置
    
    # nacos 新建配置Data ID的命名规则:
    # 默认项目名(或者: spring.cloud.nacos.config.prefix) - application.yml中的spring.profile.active - config.file-extension
    # ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
    # nacos-config-client-dev.yaml
    
    # nacos-config-client-test.yaml   ----> config.info
    

    applicaton.yml:

    spring:
      profiles:
        active: dev # 表示开发环境
    
  4. 启动类NacosConfigClientMain3377,注解@NacosConfigClientMain3377

  5. 业务类controller:ConfigClientController

    @RestController
    @RequestMapping("/config")
    @RefreshScope  //支持nacos动态刷新功能
    public class ConfigClientController {
        @Value("${config.info}")
        private String configInfo;
    
        @RequestMapping("/info")
        public String getConfigInfo(){
            return configInfo;
        }
    }
    
  6. 进入nacos页面:http://localhost:8848/nacos
    配置列表添加以下内容发布: (目前仅支持为yaml,Properties,要跟bootstrap.yml中匹配)
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第8张图片

  7. 测试启动3377,访问路径: http://localhost:3377/config/info
    nacos: 在这里插入图片描述

  8. 修改nacos配置中心内容将nacos-config-client-dev.yaml文件的版本号从1改为2,发布,重新刷新3377页面,会发现直接修改成功

18.7 Nacos 配置中心 -> 分类配置

问题 - 多环境多项目管理

  1. 实际开发中,通常一个系统会准备?

    dev开发环境
    test测试环境
    prod生产环境。

    如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢?

  2. 一个大型分布式微服务系统会有很多微服务子项目,每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境…那怎么对这些微服务配置进行管理呢?

Nacos的图形化管理界面
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第9张图片
Namespace+Group+Data lD三者关系?为什么这么设计?

  1. 是什么

    类似Java里面的package名和类名最外层的namespace是可以用于区分部署环境的,Group和DatalD逻辑上区分两个目标对象。

  2. 三者情况
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第10张图片

    默认情况:Namespace=public,Group=DEFAULT_GROUP,默认Cluster是DEFAULT

    Nacos默认的Namespace是public,Namespace主要用来实现隔离。

    比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。

    Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去

    Service就是微服务:一个Service可以包含多个Cluster (集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。

    比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,这时就可以给杭州机房的Service微服务起一个集群名称(HZ) ,给广州机房的Service微服务起一个集群名称(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。

    最后是Instance,就是微服务的实例。

三种方案:

  1. DataID方案:

    指定spring.profile.active和配置文件的DatalD来使不同环境下读取不同的配置

    默认空间+默认分组+新建dev和test两个DatalD
    在这里插入图片描述
    通过spring.profile.active属性就能进行多环境下配置文件的读取
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第11张图片
    测试: http://localhost:3377/config/info
    配置什么就加载什么!!!

  2. Group 方案:

    新建配置:DEV_GROUP下的nacos-config-client-info.yaml发布:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第12张图片
    新建配置:TEST_GROUP下的nacos-config-client-info.yaml发布:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第13张图片
    结果: 在这里插入图片描述

    修改yml: 都写在下面了

    # bootstrap.yml
    spring:
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 #Nacos服务注册中心地址
          config:
            server-addr: localhost:8848 #Nacos作为配置中心地址
            file-extension: yaml #指定yaml格式的配置
            #新增:
            group: DEV_GROUP
    	        
    # appliction.yml
    spring:
      profiles:
        active: info
      # active: test
      # active: dev # 表示开发环境
    

    访问3377: http://localhost:3377/config/info
    修改bootstrap.yml的group为TEST_GROUP,刷新3377!!!

    配置完成!!!

  3. Namespace 方案:

    访问nacos地址,

    新建 dev | test 两个namespace
    在这里插入图片描述
    在配置列表dev中,新增:(自行修改配置内容)
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第14张图片
    修改yml

    #bootstrap.yml
    spring:
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 #Nacos服务注册中心地址
          config:
            server-addr: localhost:8848 #Nacos作为配置中心地址
            file-extension: yaml #指定yaml格式的配置
            group: DEV_GROUP
            #新增:
            namespace: 37b5cf1e-5ccb-4fdb-bd8e-38cd2129301e
    
    #application.yml
    spring:
      profiles:
        active: dev # 表示开发环境
    

    访问测试:http://localhost:3377/config/info
    修改分组,在刷新3377

18.8 Nacos 集群和持久化

官网: https://nacos.io/zh-cn/docs/deployment.html
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第15张图片
我们需要mysql数据库:

默认Nacos使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。

Nacos支持三种部署模式

  • 单机模式-用于测试和单机试用。
  • 集群模式-用于生产环境,确保高可用。
  • 多集群模式-用于多数据中心场景。

单机模式支持mysql

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

  • 安装数据库,版本要求:5.6.5+
  • 初始化mysq数据库,数据库初始化文件: nacos-mysql.sql
  • 修改conf/application.properties文件,增加支持mysql数据源配置(目前只- 支持mysql),添加mysql数据源的url、用户名和密码。
    #*************** Config Module Related Configurations ***************#
    ### If use MySQL as datasource:
    spring.datasource.platform=mysql
    
    ### Count of DB:
    db.num=1
    
    ### Connect URL of DB:
    db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
    db.user.0=root
    db.password.0=root
    
    再以单机模式启动nacos,nacos所有写嵌入式数据库的数据都写到了mysql。

nacos默认存储库: derby

github的pom文件查看: https://github.com/alibaba/nacos/blob/develop/pom.xml

derby切换mysql 步骤:

  1. nacos下的conf目录下找到sql脚本:nacos-mysql.sql,在数据库执行脚本导入

  2. nacos下的conf目录下找到application.properties,将以下注释解除并修改:

  3. 重新启动Nacos,发现之前的配置全部消失 ,是因为使用的为mysql的数据库,其中什么都没有

Linux版Nacos+MySql生产环境配置:

  1. 预计需要 1个Nginx + 3个nacos注册中心 + 1个mysql
  2. Nacos下载linux版:https://github.com/alibaba/nacos/tags
  3. 安装mysql (Ubuntu OS 中Mysql的安装,操作与卸载)
  4. 安装nginx:(Ubuntu 中nginx安装)
  5. 将nacos放到linux中,解压tar -zxvf nacos-2.0.tar.gz,并且拷贝副本为mynacos

集群配置步骤:

  1. Linux 服务器上mysql数据库配置

    //进入mynacos下的conf文件夹,mysql表在此文件夹下(`nacos-mysql.sql`)
    cd mynacos/conf
    //登陆mysql
    mysql -uroot -p
    //查看所有数据库
    show databases;
    //没有数据库nacos-config的话新建
    CREATE DATABASE `nacos_config`;
    //选择数据库
    use `nacos_config`;
    //执行sql脚本
    source nacos-mysql.sql;
    //查看
    show tables;
    

    注,新的nacos比旧的多了一张permissions

  2. 修改 application.properties:

    把这些注解打开,修改数据库名称和用户名密码即可
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第16张图片

  3. 按照1,2步骤,再配置两台机器

  4. Nginx配置,由他作为负载均衡
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第17张图片

  5. 启动nginx

  6. 启动nacos

  7. 修改cloudalibaba-provider-payment9002的yml

    spring:
      cloud:
        nacos:
          discovery:
            # server-addr: 127.0.0.1:8848
            server-addr: nginx运行服务器的ip:1234
    
  8. 启动测试

19. SpringCloud Alibaba Sentinel 现实熔断与限流

gibhub: https://github.com/alibaba/Sentinel

官网文档1: https://sentinelguard.io/zh-cn/

官网文档2:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel

github文档: https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

下载地址: https://github.com/alibaba/Sentinel/tags

19.1 概述

  1. sentinel跟Hystrix差别?

    1. Hystrix 需要手动搭建监控平台
    2. Hystrix 没有web界面更加细的配置
      流控,速率控制,服务熔断,服务降级…越来越多,很麻烦
    1. sentinel 单独一个组件,可以独立出来
    2. sentinel 支持界面化细粒度统一配置
     尽量使用配置和注解少写代码
    
  2. 是什么?

    • 丰富的应用场景:哨兵接了阿里巴巴近10年的双十一大促流量的核心场景,例如杀(即爆发流量控制在系统可控制的范围)、消息削峰填谷、流量控制、实时熔断断不能应用等。
    • 您可以在实时监控中同时提供监控功能。您可以在单台实时查看接入应用的机器,甚至台下规模的500秒的汇总运行情况。
    • 广泛的开源生态:Sentinel 提供开箱即用的与其他开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。进入哨兵。
    • 改进的 SPI 扩展点:Sentinel 提供简单易用、改进的 SPI 扩展接口。您可以通过实现扩展来接口快速地自定义逻辑。例如源定制规则管理、适配动态数据等。
  3. 主要特性:(绿)
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第18张图片

  4. Sentinel 分为两个部分:

    核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。

    控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

19.2 安装运行

下载地址: https://github.com/alibaba/Sentinel/tags

运行java -jar sentinel1.8.2.jar,默认端口8080(需要java8)

web访问地址: http://localhost:8080

账户: sentinel
密码: sentinel
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第19张图片

19.3 初始化演示工程

  1. 启动nacosstartup.cmd -m standalone
  2. 启动sentinel java -jar sentinel1.8.jar
    访问路径: http://localhost:8080/
  3. 新建子项目cloudalibaba-sentinel-service8401
  4. pom (openfeign和sentinel-datasource-nacos后面要用,现在先加上)
    
    <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>cloudartifactId>
            <groupId>com.xyygroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloudalibaba-sentinel-service8401artifactId>
    
        <dependencies>
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
            dependency>
    
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
            dependency>
    
            
            <dependency>
                <groupId>com.alibaba.cspgroupId>
                <artifactId>sentinel-datasource-nacosartifactId>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-openfeignartifactId>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-devtoolsartifactId>
                <scope>runtimescope>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
            <dependency>
                <groupId>com.xyygroupId>
                <artifactId>cloud-api-commonsartifactId>
                <version>${project.version}version>
            dependency>
        dependencies>
    project>
    
  5. yml
    server:
      port: 8401
    
    spring:
      application:
        name: cloudalibaba-sentinel-service
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848 #Nacos服务注册中心地址
        sentinel:
          transport:
            dashboard: 127.0.0.1:8080 #配置Sentinel dashboard地址
            #默认8719,如果被占用会自动+1直到找到未被占用的端口
            port: 8719
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
      endpoint:
        sentinel:
          enabled: false
    
    
    feign:
      sentinel:
        enabled: true # 激活Sentinel对Feign的支持
    
  6. 启动类SentinelMain8401,注解:@EnableDiscoveryClient
  7. 业务类controller:FlowLimitController
    @RestController
    @RequestMapping("sentinel/flow")
    public class FlowLimitController {
    
    	//这个是显示在sentinel上的方法别名
        @SentinelResource("getA")
        @RequestMapping("A")
        public String testA(){
            return "---A";
        }
    
        @SentinelResource("getB")
        @RequestMapping("B")
        public String testB(){
            return "---B";
        }
    }
    
  8. 启动8401测试: http://localhost:8401/sentinel/flow/A
    访问sentinel: http://localhost:8080
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第20张图片

注: 需要先访问过8401后再查看sentinel的页面,否则是空的,因为sentinel是懒加载模式,并且如果一段时间不访问方法,则不会显示该方法

19.4 流控规则

SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第21张图片

基本介绍:

  • 资源名:唯一名称,默认请求路径。
  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)。
  • 阈值类型/单机阈值:
    • QPS(每秒钟的请求数量)︰当调用该API的QPS达到阈值的时候,进行限流。
  • 线程数:当调用该API的线程数达到阈值的时候,进行限流。
  • 是否集群:不需要集群。
  • 流控模式:
    • 直接:API达到限流条件时,直接限流。
    • 关联:当关联的资源达到阈值时,就限流自己。
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【API级别的针对来源】。
  • 流控效果:
    • 快速失败:直接失败,抛异常。
    • Warm up:根据Code Factor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
      排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效。

19.4.1 流控模式

直接:

  1. 点击簇点链路给getA添加流控规则:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第22张图片

  2. 再次访问http://localhost:8401/sentinel/flow/A,快速刷新,结果500
    (因为配置了@SentinelResource,否则页面显示Blocked by Sentinel (flow limiting),我不配置这个直接加载不到sentinel控制台)

  3. 不管是500还是Blocked by Sentinel (flow limiting),都不是想要的,这时候需要自定义报错页面,之后讲…

  4. 流控规则为并发线程数: 并发情况下,超过指定数量以外的会报错

    getA流控规则换为并发SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第23张图片

    testA方法修改:

    @RequestMapping("A")
    public String testA(){
    	ThreadUtil.sleep(800);
    	 return "---A";
    }
    

    疯狂刷新http://localhost:8401/sentinel/flow/A,结果也会报错

关联:

  • 当自己关联的资源达到阈值时,就限流自己
  • 当与A关联的资源B达到阀值后,就限流A自己(B惹事,A挂了)
  1. 将流控规则改为:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第24张图片
  2. 并发访问B(我用的是API POST)
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第25张图片
  3. 在访问 getB 的过程中浏览器访问 getA,getA出错

19.4.2 流控效果

快速失败

直接失败: 抛出异常

预热:

公式: 阈值 / coldFactor(默认3), 经过预热时长后才会到达阈值

Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考 流量控制 - Warm Up 文档,具体的例子可以参见 WarmUpFlowDemo。

通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第26张图片
源码 -> com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController


案例,阀值为10+预热时长设置5秒。

系统初始化的阀值为10/ 3约等于3,即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10

SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第27张图片
效果: 在前五秒钟疯狂刷新getB链接(超过每秒3个)会有报错信息,五秒之后疯狂刷新(低于每秒10个),则不会有任何报错信息

排队等待

匀速排队: 让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。

匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo。

该方式的作用如下图所示:
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第28张图片
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。

测试: 设置以下内容:
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第29张图片
每秒钟最多五个请求,排队访问,多出来的顺延,访问超过2000毫秒时报错

19.5 降级规则

官网: https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

慢调用比例
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第30张图片
每次未熔断时统计时长为ms,在此时间内. 响应超过RT时间被标记为慢调用,慢调用比例超过比例阈值后熔断 [取值为 0 - 1 之间] ,熔断时长为s,超过该时间取消熔断, 每秒调用数 < 最小请求数 则不启动熔断,

异常比例

SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第31张图片
在3000ms内,至少有8个请求,错误率达到60%,则熔断3s,然后重新统计时间

异常数
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第32张图片
在3000ms内,至少有8个请求,异常5个,则熔断3s,然后重新统计时间

19.6 Sentinel 热点 (服务降级)

官网: https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81

基础配置

  1. 修改8401的controller:

    	//deal_testHotKey 为降级服务名
        @SentinelResource(value = "testHotKey",blockHandler="deal_testHotKey")
        @RequestMapping("testHotKey")
        public String testHotKey(@RequestParam(value = "p1",required = false)String p1,
                                 @RequestParam(value = "p2",required = false)String p2){
            return "---testHotKey";
        }
    	//BlockException 
        public String deal_testHotKey(String p1, String p2, BlockException blockException){
            return "---deal_testHotKey---o(╥﹏╥)o"+p1+p2;
        }
    
  2. 配置sentinel:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第33张图片
    访问链接: http://localhost:8401/sentinel/flow/testHotKey?p1=a&p2=b

    解释: QPS(Queries-per-second)模式是每秒查询速率; 每秒钟,请求包含第0个参数(p1,下标从0开始,方法的参数,不是请求的参数), 访问超过3个,降级5秒

    结果: 访问无参,带参数p2,疯狂刷新没有问题,而全参带p1和p2与带p1的请求如果触碰到热点规则则会熔断

高级配置
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第34张图片
p1=浪的时候限流阈值为10,其他的还是3;

注: 支持基本类型和String类型,参数的值=指定值,限流阈值更改(中文需要URL编码改一下)
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第35张图片

testHotKey方法抛异常,加入以下代码int age =10/0;运行测试???

将会直接500,并不会进入兜底方法

注:
@SentinelResource - 处理的是sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;
RuntimeException int age = 10/0,这个是java运行时报出的运行时异常RunTimeException,@SentinelResource不管
后面会讲fallback可以配

19.7 系统限流

官网: https://github.com/alibaba/Sentinel/wiki/系统自适应限流

简介:

  • 从整体维度对应用入口流量进行控制

支持模式:

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第36张图片

19.8 @SentinelResource

上述兜底方案面领的问题:

  • 系统默认的,没有体现我们自己的业务要求。
  • 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
  • 每个业务方法都添加—个兜底的,那代码膨胀加剧。
  • 全局统—的处理方法没有体现。

19.8.1 自定义限流处理逻辑

  1. 自定义公有兜底类:
    public class CustomerBlockHandler {
    
        public static Output handlerException(BlockException exception){
            return Output.failure(444,exception.getClass().getCanonicalName()+"\t客户自定义兜底方法=========1");
        }
    
        public static Output handlerException2(BlockException exception){
            return Output.failure(444,exception.getClass().getCanonicalName()+"\t客户自定义兜底方法==========2");
        }
    }
    
  2. 新增controller方法:
    	// blockHandlerClass = 兜底类,blockHandler = 兜底类中的那个方法?
        @SentinelResource(value = "customerBlockHandler",
                blockHandlerClass = CustomerBlockHandler.class,
                blockHandler = "handlerException2")
        @GetMapping("/customerBlockHandler")
        public Output byURL1(){
            return Output.success("自定义  限流成功",new Payment(2020L,"serial001"));
        }
    
  3. 增加限流规则:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第37张图片
  4. 访问测试: 快速刷新http://localhost:8401/sentinel/rate/customerBlockHandler

19.8.2 更多注解说明

地址:https://github.com/alibaba/Sentinel/wiki/注解支持sentinelresource-注解

注意:注解方式埋点不支持 private 方法。
  • 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 逻辑中,而是会原样抛出。

19.9 服务熔断

sentinel 整合 ribbon + openFeign + fallback

两种降级方法

  • fallback 运行异常降级配置

  • blockHandler sentinel控制台异常降级配置

    @SentinelResource(value = "test",blockHandler = "blockHandler降级方法",fallback ="fallback降级方法")
    

19.9.1 -> 熔断之 Ribbon 系列:

SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第38张图片

19.9.1 程序准备

准备三个子项目:

cloudalibaba-provider-payment9003 服务提供者
cloudalibaba-provider-payment9004 服务提供者

cloudalibaba-consumer-nacos-order84 服务消费者

服务提供者:

  1. 新建子项目cloudalibaba-provider-payment9003cloudalibaba-provider-payment9004的集群
  2. pom
    
    <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>cloudartifactId>
            <groupId>com.xyygroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloudalibaba-provider-payment9003artifactId>
    
        <dependencies>
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
            <dependency>
                <groupId>log4jgroupId>
                <artifactId>log4jartifactId>
            dependency>
            <dependency>
                <groupId>com.xyygroupId>
                <artifactId>cloud-api-commonsartifactId>
                <version>${project.version}version>
            dependency>
        dependencies>
    project>
    
  3. yml
    server:
      port: 9003 #9004
    
    spring:
      application:
        name: nacos-payment-provider
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 #配置Nacos地址
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
  4. 启动类:PaymentMain9003/9004 ,注解:@EnableDiscoveryClient
  5. 业务类controller:PaymentController
    @RestController
    public class PaymentController {
        @Value("${server.port}")
        private String serverPort;
    
        //模拟数据库
        public static HashMap<Long, Payment> hashMap = new HashMap<>();
        static
        {
            hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
            hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
            hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
        }
    
        @GetMapping(value = "/paymentSQL/{id}")
        public Output<Payment> paymentSQL(@PathVariable("id") Long id)
        {
            Payment payment = hashMap.get(id);
            Output<Payment> result = Output.success("from mysql,serverPort:  "+serverPort,payment);
            return result;
        }
    }
    

服务消费者

  1. 新建子项目cloudalibaba-consumer-nacos-order84

  2. pom

    
    <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>cloudartifactId>
            <groupId>com.xyygroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloudalibaba-consumer-nacos-order84artifactId>
    
        <dependencies>
            
            
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
            dependency>
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
            
            <dependency>
                <groupId>com.xyygroupId>
                <artifactId>cloud-api-commonsartifactId>
                <version>${project.version}version>
            dependency>
        dependencies>
    
    project>
    
  3. yml

    server:
      port: 84
    
    spring:
      application:
        name: nacos-order-consumer
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        sentinel:
          transport:
            #配置Sentinel dashboard地址
            dashboard: localhost:8080
            #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
            port: 8719
    
    #消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
    service-url:
      nacos-user-service: http://nacos-payment-provider
    
    # 激活Sentinel对Feign的支持
    feign:
      sentinel:
        enabled: true
    
  4. 启动类 OrderNacosMain84,注解:@EnableDiscoveryClient

  5. 配置类config:ApplicationContextConfig

    @Configuration
    public class ApplicationContextConfig {
    
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    
  6. 业务类controller:CircleBreakerController

    @RestController
    public class CircleBreakerController {
        public static final String SERVICE_URL = "http://nacos-payment-provider";
    
        @Resource
        private RestTemplate restTemplate;
    
        @RequestMapping("/consumer/fallback/{id}")
        @SentinelResource(value = "fallback")//没有配置
        public Output<Payment> fallback(@PathVariable Long id)
        {
            Output<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,Output.class,id);
    
            if (id == 4) {
                throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
            }else if (result.getData() == null) {
                throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
            }
            return result;
        }
    }
    

19.9.2 运行错误降级

只加fallback

  1. 修改84的controller
    @RestController
    public class CircleBreakerController {
        public static final String SERVICE_URL = "http://nacos-payment-provider";
    
        @Resource
        private RestTemplate restTemplate;
    
        @RequestMapping("/consumer/fallback/{id}")
        //@SentinelResource(value = "fallback")//没有配置
        @SentinelResource(value="fallback",fallback = "handlerFallback") //运行异常 降级方法
        public Output<Payment> fallback(@PathVariable Long id)
        {
            Output<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,Output.class,id);
    
            if (id == 4) {
                throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
            }else if (result.getData() == null) {
                throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
            }
            return result;
        }
        //Throwable 可以将报错信息带过来
        public Output handlerFallback(Long id,Throwable e)
        {
            return Output.failure(444,"兜底异常 handlerFallback,exception内容 \t id:"+id+"\t"+e.getMessage());
        }
    
    }
    
  2. 测试: http://localhost:84/consumer/fallback/4
    结果:在这里插入图片描述

只加 blockHandler

  1. 修改84controller:
    @RestController
    public class CircleBreakerController {
        public static final String SERVICE_URL = "http://nacos-payment-provider";
    
        @Resource
        private RestTemplate restTemplate;
    
        @RequestMapping("/consumer/fallback/{id}")
        //@SentinelResource(value = "fallback")//没有配置
        //@SentinelResource(value="fallback",fallback = "handlerFallback") //运行异常 降级方法
        @SentinelResource(value = "fallback",blockHandler = "blockHandler")     //sentinel 控制台配置违规
        public Output<Payment> fallback(@PathVariable Long id)
        {
            Output<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,Output.class,id);
    
            if (id == 4) {
                throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
            }else if (result.getData() == null) {
                throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
            }
            return result;
        }
    //    //Throwable 可以将报错信息带过来
    //    public Output handlerFallback(Long id,Throwable e)
    //    {
    //        return Output.failure(444,"兜底异常 handlerFallback,exception内容 \t id:"+id+"\t"+e.getMessage());
    //    }
    
        public Output blockHandler(Long id, BlockException e)
        {
            return Output.failure(444,"兜底异常 blockHandler,exception内容   id:"+id+"   "+e.getMessage());
        }
    }
    
  2. 配置sentinel:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第39张图片
  3. 运行测试:
    正确参数:http://localhost:84/consumer/fallback/1快速刷新会进入兜底方法
    错误参数:http://localhost:84/consumer/fallback/4 快速刷新会进入兜底方法,慢速刷新会爆500的错误

fallback和blockHandler都配置

  1. 修改84 controller

    @RestController
    public class CircleBreakerController {
        public static final String SERVICE_URL = "http://nacos-payment-provider";
    
        @Resource
        private RestTemplate restTemplate;
    
        @RequestMapping("/consumer/fallback/{id}")
        //@SentinelResource(value = "fallback")//没有配置
        //@SentinelResource(value="fallback",fallback = "handlerFallback") //运行异常 降级方法
        //@SentinelResource(value = "fallback",blockHandler = "blockHandler")     //sentinel 控制台配置违规
        @SentinelResource(value="fallback",fallback = "handlerFallback",blockHandler = "blockHandler")
        public Output<Payment> fallback(@PathVariable Long id)
        {
            Output<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,Output.class,id);
    
            if (id == 4) {
                throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
            }else if (result.getData() == null) {
                throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
            }
            return result;
        }
        //Throwable 可以将报错信息带过来
        //运行异常兜底
        public Output handlerFallback(Long id,Throwable e)
        {
            return Output.failure(444,"兜底异常 handlerFallback,exception内容    id:"+id+"    "+e.getMessage());
        }
    	//sentinel控制台违规兜底
        public Output blockHandler(Long id, BlockException e)
        {
            return Output.failure(444,"兜底异常 blockHandler,exception内容   id:"+id+"   "+e.getMessage());
        }
    }
    
  2. sentinel配置:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第40张图片

  3. 测试:
    参数正确: http://localhost:84/consumer/fallback/1快速访问会进入blockHandler兜底方法(sentinel配置)
    错误参数:http://localhost:84/consumer/fallback/4快速访问,会进入blockHandler兜底方法,而慢速访问则会因为参数不正确抛出异常进入handlerFallback兜底方法

19.9.3 异常特例

exceptionsToIgnore 出现什么异常不降级
exceptionsToTrace 出现什么异常降级

@SentinelResource(value="fallback",fallback = "handlerFallback",exceptionsToIgnore/exceptionsToTrace = {IllegalArgumentException.class} )

19.9.4 -> 熔断之 openFeign 系列:

  • 84消费者调用提供者9003
  • openFeign一般是消费侧

修改84

  1. pom: 加入以下openfeign依赖
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    
  2. pom: 加入以下支持
    # 激活Sentinel对Feign的支持
    feign:
      sentinel:
        enabled: true
    
  3. 启动类加入注解:
    import org.springframework.cloud.openfeign.EnableFeignClients;
    @EnableFeignClients
    
  4. 创建service:
    PaymentService 接口
    //fallback  服务报错降级类
    @FeignClient(value = "nacos-payment-provider",fallback = PaymentServiceFallback.class)
    public interface PaymentService {
    
        @GetMapping(value = "/paymentSQL/{id}")
        Output<Payment> paymentSQL(@PathVariable("id")Long id);
    }
    
    PaymentServiceFallback 实现接口
    //千万不要忘记注解
    @Component
    public class PaymentServiceFallback implements PaymentService{
    
        @Override
        public Output<Payment> paymentSQL(Long id) {
            return Output.failure(400,"openfeign服务降级返回-->> PaymentServiceFallback");
        }
    }
    
  5. 创建controller:FeignOrderController
    @RestController
    @RequestMapping("/consumer/feign")
    public class FeignOrderController {
        @Resource
        public PaymentService paymentService;
    
        @GetMapping("/getSQL/{id}")
        public Output<Payment> paymentSQL(@PathVariable("id")Long id){
            return paymentService.paymentSQL(id);
        }
    }
    
  6. 测试:http://localhost:84/consumer/feign/getSQL/1
    关闭服务端9003,9004后在次测试在这里插入图片描述

19.10 熔断框架比较

- Sentinel Hystrix resilience4j
隔离策略 信号量隔离(并发线程数限流) 线程池隔商/信号量隔离 信号量隔离
熔断降级策略 基于响应时间、异常比率、异常数 基于异常比率 基于异常比率、响应时间
实时统计实现 滑动窗口(LeapArray) 滑动窗口(基于RxJava) Ring Bit Buffer
动态规则配置 支持多种数据源 支持多种数据源 有限支持
扩展性 多个扩展点 插件的形式 接口的形式
基于注解的支持 支持 支持 支持
限流 基于QPS,支持基于调用关系的限流 有限的支持 Rate Limiter
流量整形 支持预热模式匀速器模式、预热排队模式 不支持 简单的Rate Limiter模式
系统自适应保护 支持 不支持 不支持
控制台 提供开箱即用的控制台,可配置规则、查看秒级监控,机器发观等 简单的监控查看 不提供控制台,可对接其它监控系统

19.11 持久化(8401)

这个持久化要跟alibaba的nacos集成的,而且感觉不完整,因为目前只支持简单的流控,降级,热点,系统,授权等还未完善…可以自己写代码完善…

是什么?

一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化。

怎么玩?

将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效。

步骤

修改8401:

  1. pom: 加入以下依赖

    
    <dependency>
        <groupId>com.alibaba.cspgroupId>
        <artifactId>sentinel-datasource-nacosartifactId>
    dependency>
    
  2. yml

    spring:
      cloud:
        sentinel:
          datasource: #<---------------------------关注点,添加Nacos数据源配置
            ds1:
              nacos:
                server-addr: localhost:8848  #数据在那个nacos中
                dataId: cloudalibaba-sentinel-service	#规则名
                groupId: DEFAULT_GROUP 	#规则在那个分组
                data-type: json	#数据类型
                rule-type: flow	#规则类型:流动
    
  3. 打开nacos:配置规则:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第41张图片

    [{
        "resource": "byUrl",
        "IimitApp": "default",
        "grade": 1,
        "count": 1, 
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }]
    

    解释: Data ID: 8401 的 spring.application.name
    选择JSON,

    • resource:资源名称;(@SentinelResource(value))的value
    • limitApp:来源应用;
    • grade:阈值类型,0表示线程数, 1表示QPS;
    • count:单机阈值;
    • strategy:流控模式,0表示直接,1表示关联,2表示链路;
    • controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
    • clusterMode:是否集群。
  4. 重启8401,访问:http://localhost:8401/sentinel/rate/byURL快速访问,会发现直接被限流
    查看sentinel的控制台: 有一条记录
    在这里插入图片描述

持久化过程: nacos跟项目配置相同规则 --> 启动项目 --> 项目会从nacos拿配置下来 --> 放入sentinel中 --> 服务停止 --> sentinel会把该服务的所有配置清空 --> 服务再次重启 --> 将会再次从nacos拿下来,配置进sentinel

json示例:

[{
    "resource": "byURL",
    "IimitApp": "default",
    "grade": 1,
    "count": 1, 
    "strategy": 0,
    "controlBehavior": 0,
    "clusterMode": false
},{
    "resource": "customerBlockHandler",
    "IimitApp": "default",
    "grade": 1,
    "count": 1, 
    "strategy": 0,
    "controlBehavior": 0,
    "clusterMode": false 
}]

当nacos的规则修改之后会自动推送到sentinel中,并不需要重启任何服务或者sentinel

20. SpringCloud Alibaba Seata处理分布式事务

官网: https://seata.io/zh-cn/

github: https://github.com/seata/seata

下载地址1: https://seata.io/zh-cn/blog/download.html

下载地址2(): https://github.com/seata/seata/tags

20.1 分布式事务问题由来

分布式前

  • 单机单库没这个问题
  • 从1:1 -> 1:N -> N:N

单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三三 个服务来完成。此时每个服务内部的数据一致性由本地事务来保证, 但是全局的数据一致性问题没法保证。

SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第42张图片

一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题。

20.2 简介

以下很多代码的东西都能在下载的压缩包的conf目录下的README-zh.md找到解释,可以去看源码,或者拿sql,老版本的会在conf下有,新版本的只能去github上自己拿了

  1. 能干嘛?

    一个典型的分布式事务过程

    分布式事务处理过程的 一个ID+三组件模型

    Transaction ID XID 全局唯一的事务ID
    三组件概念

    • TC (Transaction Coordinator) - 事务协调者
      维护全局和分支事务的状态,驱动全局事务提交或回滚。
    • TM (Transaction Manager) - 事务管理器
      定义全局事务的范围:开始全局事务、提交或回滚全局事务。
    • RM (Resource Manager) - 资源管理器
      管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
  2. 处理过程?

    TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID;

    XID在微服务调用链路的上下文中传播;

    RM向TC注册分支事务,将其纳入XID对应全局事务的管辖;

    TM向TC发起针对XID的全局提交或回滚决议;

    TC调度XID下管辖的全部分支事务完成提交或回滚请求。
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第43张图片

  3. 使用?

    本地 @Transactional (spring的)

    全局 @GlobalTransactional (springcloud alibaba的)

    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第44张图片

    只需要使用一个 @GlobalTransactional 注解在业务方法上:

20.3 下载 | 安装 | 配置mysql

官网: https://seata.io/zh-cn/

  1. 下载
    地址: https://github.com/seata/seata/tags(我自己1.4)
    每个版本的配置可能有差异,详情请看 官网参考文档

  2. 数据源配置为mysql

    seata所有配置1.4.2源文件: https://gitee.com/xyy-kk_admin/springcloud-config/tree/master/seata-conf

    修改conf下的 file.conf 文件(提前备份)

    点我查看,官网完整配置示例

    store{
    	mode="db"
    	db {
        ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
        datasource = "druid"
        ## mysql/oracle/postgresql/h2/oceanbase etc.
        dbType = "mysql"
        driverClassName = "com.mysql.cj.jdbc.Driver"
    	#这里一定要加时区,否则报错(格林+8)
    	#格林威治时间 ( GMT ),这(UTC)是从英国格林威治零经度线上测得的。
        url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true&serverTimezone=GMT%2B8"
        user = "root"
        password = "root"
      }
    }
    service {
      #事务服务组映射,后缀(_tx_group)
      vgroupMapping.my_test_tx_group = "xyy_tx_group"
      #只支持注册时。Type =file,请不要设置多个地址
      default.grouplist = "127.0.0.1:8091"
      #降级,当前不支持
      enableDegrade = false
      #禁用seata
      disableGlobalTransaction = false
    }
    
  3. 修改registry.conf文件(提前备份)

    registry {
      type = "nacos"
      nacos {
      	# nacos注册服务名
        application = "seata-server"
        #nacos地址
        serverAddr = "127.0.0.1:8848"
        #SEATA_GROUP nacos服务名称
        group = "SEATA_GROUP"
        #命名空间
        namespace = ""
        cluster = "default"
        username = "nacos"
        password = "nacos"
      }
    }
    
  4. mysql创建数据库:

    CREATE DATABASE seata

  5. 导入表:

    20.2 简介第一行

    SQL获取地址: https://gitee.com/xyy-kk_admin/springcloud-config/tree/master/seata-sql自行导入…

    linux 可以先把文件放到服务器中,在脚本目录中登陆mysqlmysql -uroot -p,然后执行use seata选择seata数据库执行导入命令source mysql.sql

  6. 启动服务:

    要先启动nacos再启动seata,别搞错了,因为Seata要注册进入nacos

    window:
    nacos --> startup.cmd -m standalone
    seata --> cmd或双击: seata-server.bat

    linux
    nacos -->sh startup.sh -m standalone
    seata --> sh seata-server.sh

  7. 查看服务:登陆naocs
    在这里插入图片描述

20.4 订单/库存/账户业务数据准备

以下需要nacos和seata都启动成功配置好mysql!!!

分布式业务说明:

  • 这里我们会创建三个服务,一个订单服务,一个库存服务,一个账户服务。

  • 当用户下单时,会在订单服务中创建一个订单, 然后通过远程调用库存服务来扣减下单商品的库存,再通过远程调用账户服务来扣减用户账户里面的余额,最后在订单服务中修改订单状态为已完成。

  • 该操作跨越三个数据库,有两次远程调用,很明显会有分布式事务问题。

    下订单—>扣库存—>减账户(余额)。

数据库 and 表 创建步骤:

完整SQL地址: 完整执行SQL即可不看下面的代码跳到20.5 订单/库存/账户微服务准备

数据库结构(成品):
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第45张图片

  1. 创建数据库:
    seata_ order:存储订单的数据库;
    seata_ storage:存储库存的数据库;
    seata_ account:存储账户信息的数据库。

    CREATE DATABASE seata_order;
    CREATE DATABASE seata_storage;
    CREATE DATABASE seata_account;
    
  2. seata_order库下建 t_order 表

    USE `seata_order`;
    CREATE TABLE t_order (
        `id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
        `user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
        `product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
        `count` INT(11) DEFAULT NULL COMMENT '数量',
        `money` DECIMAL(11,0) DEFAULT NULL COMMENT '金额',
        `status` INT(1) DEFAULT NULL COMMENT '订单状态: 0:创建中; 1:已完结'
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;
    
  3. seata_storage 库下建 t_storage 表,并插入一条数据

    USE `seata_storage`;
    CREATE TABLE t_storage (
    	`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    	`product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',
    	`total` INT(11) DEFAULT NULL COMMENT '总库存',
    	`used` INT(11) DEFAULT NULL COMMENT '已用库存',
    	`residue` INT(11) DEFAULT NULL COMMENT '剩余库存'
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;
    
    INSERT INTO seata_storage.t_storage(`id`, `product_id`, `total`, `used`, `residue`)
    VALUES ('1', '1', '100', '0','100');
    
  4. seata_account 库下建 t_account 表,并插入一条数据

    USE `seata_account`;
    CREATE TABLE t_account(
    	`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',
    	`user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',
    	`total` DECIMAL(10.0) DEFAULT NULL COMMENT '总额度',
    	`used` DECIMAL(10.0) DEFAULT NULL COMMENT '已用余额',
    	`residue` DECIMAL(10.0) DEFAULT '0' COMMENT '剩余可用额度'
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;
    
    INSERT INTO seata_account.t_account(`id`, `user_id`, `total`, `used`, `residue`)
    VALUES ('1', '1', '1000', '0', '1000');
    
  5. 回滚日志表

    官网地址: https://github.com/seata/seata/blob/develop/script/client/at/db/mysql.sql

    订单(order)-库存(storage)-账户(account)3个库下都需要键各自的回滚日志表

    #  USE `seata_storage`;
    #  USE `seata_account`;
    USE `seata_order`;
    CREATE TABLE IF NOT EXISTS `undo_log`
    (
        `branch_id`     BIGINT       NOT NULL COMMENT '部门事务 id',
        `xid`           VARCHAR(128) NOT NULL COMMENT '全局事务 id',
        `context`       VARCHAR(128) NOT NULL COMMENT '撤消日志上下文,如序列化',
        `rollback_info` LONGBLOB     NOT NULL COMMENT '回滚信息',
        `log_status`    INT(11)      NOT NULL COMMENT '0:正常状态,1:防御状态',
        `log_created`   DATETIME(6)  NOT NULL COMMENT '创建时间',
        `log_modified`  DATETIME(6)  NOT NULL COMMENT '最新修改时间',
        UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
    ) ENGINE = INNODB
      AUTO_INCREMENT = 1
      DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
    

20.5 订单/库存/账户微服务准备

下订单 -> 减库存 -> 扣余额 -> 改(订单)状态

20.5.1 订单Order-Module

项目结构:
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第46张图片

  1. 新建子项目seata-order-service2001

  2. pom

    
    <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://maven.apache.org/POM/4.0.0"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>cloudartifactId>
            <groupId>com.xyygroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>seata-order-service2001artifactId>
    
        <dependencies>
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
            dependency>
            
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-alibaba-seataartifactId>
            dependency>
            <dependency>
                <groupId>io.seatagroupId>
                <artifactId>seata-allartifactId>
                <version>1.4.2version>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-openfeignartifactId>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
    
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
            dependency>
    
            <dependency>
                <groupId>org.mybatis.spring.bootgroupId>
                <artifactId>mybatis-spring-boot-starterartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
        dependencies>
    project>
    
  3. yml

    tx-service-group的值请去查看20.3 配置文件的file.conf 下的service

    server:
      port: 2001
    
    spring:
      application:
        name: seata-order-service
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        alibaba:
          seata:
            #这个在 conf文件下的 service.vgroupMapping.my_test_tx_group = "xyy_tx_group"
            tx-service-group: xyy_tx_group
      datasource:
        #数据库8.0的配置方式
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
        username: root
        password: root
    #一定要false吧hystrix服务降级关了,否则如果报错全局事务将无法进行
    feign:
      hystrix:
        enabled: false
    
    logging:
      level:
        io:
          seata: info
    
    mybatis:
      type-aliases-package: com.xyy.springcloudalibaba.entity
      mapperLocations: classpath:mapper/*.xml
    
  4. conf
    跟application.yml同级

    file.conf
    注意更改vgroupMapping.后面的值
    disableGlobalTransaction 一定要等于false才是打开事务,true是关闭事务

    transport {
      # tcp, unix-domain-socket
      type = "TCP"
      #NIO, NATIVE
      server = "NIO"
      #enable heartbeat
      heartbeat = true
      # the client batch send request enable
      enableClientBatchSendRequest = true
      #thread factory for netty
      threadFactory {
        bossThreadPrefix = "NettyBoss"
        workerThreadPrefix = "NettyServerNIOWorker"
        serverExecutorThread-prefix = "NettyServerBizHandler"
        shareBossWorker = false
        clientSelectorThreadPrefix = "NettyClientSelector"
        clientSelectorThreadSize = 1
        clientWorkerThreadPrefix = "NettyClientWorkerThread"
        # netty boss thread size
        bossThreadSize = 1
        #auto default pin or 8
        workerThreadSize = "default"
      }
      shutdown {
        # when destroy server, wait seconds
        wait = 3
      }
      serialization = "seata"
      compressor = "none"
    }
    service {
      #transaction service group mapping
      vgroupMapping.xyy_tx_group = "default"
      #only support when registry.type=file, please don't set multiple addresses
      default.grouplist = "127.0.0.1:8091"
      #degrade, current not support
      enableDegrade = false
      #disable seata
      disableGlobalTransaction = false
    }
    
    client {
      rm {
        asyncCommitBufferLimit = 10000
        lock {
          retryInterval = 10
          retryTimes = 30
          retryPolicyBranchRollbackOnConflict = true
        }
        reportRetryCount = 5
        tableMetaCheckEnable = false
        tableMetaCheckerInterval = 60000
        reportSuccessEnable = false
        sagaBranchRegisterEnable = false
        sagaJsonParser = jackson
        sagaRetryPersistModeUpdate = false
        sagaCompensatePersistModeUpdate = false
        tccActionInterceptorOrder = -2147482648 #Ordered.HIGHEST_PRECEDENCE + 1000
      }
      tm {
        commitRetryCount = 5
        rollbackRetryCount = 5
        defaultGlobalTransactionTimeout = 60000
        degradeCheck = false
        degradeCheckPeriod = 2000
        degradeCheckAllowTimes = 10
        interceptorOrder = -2147482648 #Ordered.HIGHEST_PRECEDENCE + 1000
      }
      undo {
        dataValidation = true
        onlyCareUpdateColumns = true
        logSerialization = "jackson"
        logTable = "undo_log"
        compress {
          enable = true
          # allow zip, gzip, deflater, 7z, lz4, bzip2, default is zip
          type = zip
          # if rollback info size > threshold, then will be compress
          # allow k m g t
          threshold = 64k
        }
      }
      loadBalance {
          type = "RandomLoadBalance"
          virtualNodes = 10
      }
    }
    log {
      exceptionRate = 100
    }
    

    registry.conf:
    都注册进入nacos

    registry {
      # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa、custom
      type = "nacos"
    
      nacos {
        application = "seata-server"
        serverAddr = "127.0.0.1:8848"
        group = "SEATA_GROUP"
        namespace = ""
        username = "nacos"
        password = "nacos"
      }
      eureka {
        serviceUrl = "http://localhost:8761/eureka"
        weight = "1"
      }
      redis {
        serverAddr = "localhost:6379"
        db = "0"
        password = ""
        timeout = "0"
      }
      zk {
        serverAddr = "127.0.0.1:2181"
        sessionTimeout = 6000
        connectTimeout = 2000
        username = ""
        password = ""
      }
      consul {
        serverAddr = "127.0.0.1:8500"
        aclToken = ""
      }
      etcd3 {
        serverAddr = "http://localhost:2379"
      }
      sofa {
        serverAddr = "127.0.0.1:9603"
        region = "DEFAULT_ZONE"
        datacenter = "DefaultDataCenter"
        group = "SEATA_GROUP"
        addressWaitTime = "3000"
      }
      file {
        name = "file.conf"
      }
      custom {
        name = ""
      }
    }
    
    config {
      # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig、custom
      type = "nacos"
    
      nacos {
        serverAddr = "127.0.0.1:8848"
        namespace = ""
        group = "SEATA_GROUP"
        username = "nacos"
        password = "nacos"
        dataId = "seata.properties"
      }
      consul {
        serverAddr = "127.0.0.1:8500"
        aclToken = ""
      }
      apollo {
        appId = "seata-server"
        apolloMeta = "http://192.168.1.204:8801"
        namespace = "application"
        apolloAccesskeySecret = ""
      }
      zk {
        serverAddr = "127.0.0.1:2181"
        sessionTimeout = 6000
        connectTimeout = 2000
        username = ""
        password = ""
        nodePath = "/seata/seata.properties"
      }
      etcd3 {
        serverAddr = "http://localhost:2379"
      }
      file {
        name = "file.conf"
      }
      custom {
        name = ""
      }
    }
    
  5. 实体类entity:

    output 实体类点我

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Order {
        private Long id;
        private Long userId;
        private Long productId;
        private Integer count;
        private BigDecimal money;
        private Integer status; //订单状态:0:创建中;1:已完结
    }
    
  6. 接口 OrderMapper 和OrderMapper.xml

    import com.xyy.springcloudalibaba.entity.Order;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Param;
    @Mapper
    public interface OrderMapper {
        //1, 新建订单
        void create(Order order);
        //2. 修改订单状态,0-->1
        Integer update(@Param("id") Long id);
    }
    

    xml: 按照自己的路径修改

    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    
    <mapper namespace="com.xyy.springcloudalibaba.mapper.OrderMapper">
    
        <resultMap id="BaseResultMap" type="com.xyy.springcloudalibaba.entity.Order">
            <id column="id" property="id" jdbcType="BIGINT"/>
            <result column="user_id" property="userId" jdbcType="BIGINT"/>
            <result column="product_id" property="productId" jdbcType="BIGINT"/>
            <result column="count" property="count" jdbcType="INTEGER"/>
            <result column="money" property="money" jdbcType="DECIMAL"/>
            <result column="status" property="status" jdbcType="INTEGER"/>
        resultMap>
    
        <insert id="create" useGeneratedKeys="true" keyProperty="id"
                parameterType="com.xyy.springcloudalibaba.entity.Order">
            insert into t_order (id, user_id, product_id, count, money, status)
            values (null, #{userId}, #{productId}, #{count}, #{money}, 0);
        insert>
        
        <update id="update">
            update t_order
            set status = 1
            where id = ${id}
        update>
    mapper>
    
  7. 业务类 service:

    OrderService:

    import com.xyy.springcloudalibaba.entity.Order;
    
    public interface OrderService {
        Long create(Order order);
    }
    

    AccountFeignService:

    import com.xyy.springcloudalibaba.entity.Output;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import java.math.BigDecimal;
    
    @FeignClient(value = "seata-account-service")
    public interface AccountFeignService {
    	//使用说明方式去访问其他服务的方法
        @PostMapping(value = "/account/decrease")
        Output decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
    }
    

    StorageFeignService:

    import com.xyy.springcloudalibaba.entity.Output;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @FeignClient(value = "seata-storage-service")
    public interface StorageFeignService {
    	//使用说明方式去访问其他服务的方法
        @PostMapping(value = "/storage/decrease")
        Output decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
    
    }
    

    OrderServiceImpl:

    import com.xyy.springcloudalibaba.entity.Order;
    import com.xyy.springcloudalibaba.entity.Output;
    import com.xyy.springcloudalibaba.mapper.OrderMapper;
    import com.xyy.springcloudalibaba.service.AccountFeignService;
    import com.xyy.springcloudalibaba.service.OrderService;
    import com.xyy.springcloudalibaba.service.StorageFeignService;
    import io.seata.spring.annotation.GlobalTransactional;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    
    @Service
    @Slf4j
    public class OrderServiceImpl implements OrderService {
        @Resource
        private OrderMapper orderMapper;
        @Resource
        private StorageFeignService storageFeignService;
        @Resource
        private AccountFeignService accountFeignService;
    
        /**
         * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
         * 简单说:下订单->扣库存->减余额->改状态
         */
        @Override
        //@GlobalTransactional   //暂时不要启动这个注解
        public Long create(Order order) {
            log.info("----->开始新建订单");
            //1 新建订单
            orderMapper.create(order);
            log.info("----->订单id为:" + order.getId());
            //2 扣减库存
            log.info("----->订单微服务开始调用库存,做扣减Count");
            storageFeignService.decrease(order.getProductId(), order.getCount());
            log.info("----->订单微服务开始调用库存,做扣减end");
    
            //3 扣减账户
            log.info("----->订单微服务开始调用账户,做扣减Money");
    		accountFeignService.decrease(order.getUserId(), order.getMoney());
            log.info("----->订单微服务开始调用账户,做扣减end");
    
            //4 修改订单状态,从零到1,1代表已经完成
            log.info("----->修改订单状态开始");
            orderMapper.update(order.getId());
            log.info("----->修改订单状态结束");
    
            log.info("----->下订单结束了,O(∩_∩)O哈哈~");
            return order.getId();
        }
    }
    
  8. 业务类controller:OrderController

    import com.xyy.springcloudalibaba.entity.Order;
    import com.xyy.springcloudalibaba.entity.Output;
    import com.xyy.springcloudalibaba.service.OrderService;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    @RestController
    @RequestMapping("order")
    public class OrderController {
    
        @Resource
        private OrderService orderService;
    
        //http://localhost:2001/order/create?userId=1&productId=1&cout=10&money=100
        @GetMapping("create")
        public Output create(Order order) {
            Long orderid = orderService.create(order);
            return Output.success("成功", "订单id: " + orderid);
        }
    }
    
  9. 配置类:

    MyBatisConfig:

    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @MapperScan("com.xyy.springcloudalibaba.mapper")
    public class MyBatisConfig {
    }
    

    DataSourceProxyConfig:

    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.beans.factory.annotation.Value;
    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;
    
    
    /**
     * 使用Seata对数据源进行代理
     */
    @Configuration
    public class DataSourceProxyConfig {
    	
    	//mybatis.mapperLocations在yml里定义的 mapper.xml路径
        @Value("${mybatis.mapperLocations}")
        private String mapperLocations;
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource")
        public DataSource druidDataSource() {
            return new DruidDataSource();
        }
    
        @Bean
        public DataSourceProxy dataSourceProxy(DataSource dataSource) {
            return new DataSourceProxy(dataSource);
        }
    
        @Bean
        public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dataSourceProxy);
            sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
            sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
            return sqlSessionFactoryBean.getObject();
        }
    }
    
  10. 启动类: SeataOrderMain2001

    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;
    
    @EnableDiscoveryClient  //注册nacos启动
    @EnableFeignClients	//feign启动
    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)  //排除自带的数据源配置,用自己配置的
    public class SeataOrderMain2001 {
        public static void main(String[] args) {
            SpringApplication.run(SeataOrderMain2001.class, args);
        }
    }
    

启动测试没有问题(不要打开order实现类中的那个注解)

20.5.2 库存 storage-Module

项目结构:
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第47张图片

  1. 创建子项目seata-storage-service2002

  2. pom跟seata-order-service2001的pom相同

  3. yml
    除了这些不一样以外,其他一模一样

    server:
      port: 2002
    
    spring:
      application:
        name: seata-storage-service
      datasource:
        # 数据库变了`seata_storage`
        url: jdbc:mysql://localhost:3306/seata_storage?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
    
  4. file.confregistry.confseata-order-service2001的一模一样

  5. 实体类entity:

    output 实体类点我

    Storage

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Storage {
    
        private Long id;
    
        /**
         * 产品id
         */
        private Long productId;
    
        /**
         * 总库存
         */
        private Integer total;
    
        /**
         * 已用库存
         */
        private Integer used;
    
        /**
         * 剩余库存
         */
        private Integer residue;
    }
    
  6. mapper :

    @Mapper
    public interface StorageMapper {
        //扣减库存
        Integer decrease(@Param("productId") Long productId, @Param("count") Integer count);
    }
    

    xml

    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    
    <mapper namespace="com.xyy.springcloudalibaba.mapper.StorageMapper">
    
        <resultMap id="BaseResultMap" type="com.xyy.springcloudalibaba.entity.Storage">
            <id column="id" property="id" jdbcType="BIGINT"/>
            <result column="product_id" property="productId" jdbcType="BIGINT"/>
            <result column="total" property="total" jdbcType="INTEGER"/>
            <result column="used" property="used" jdbcType="INTEGER"/>
            <result column="residue" property="residue" jdbcType="INTEGER"/>
        resultMap>
    
        <update id="decrease">
            UPDATE
                t_storage
            SET used    = used + #{count},
                residue = residue - #{count}
            WHERE product_id = #{productId}
        update>
    
    mapper>
    
  7. 业务类 service:

    StorageService:

    public interface StorageService {
        //扣减库存
        void decrease(Long productId, Integer count);
    }
    

    StorageServiceImpl:

    @Service
    @Slf4j
    public class StorageServiceImpl implements StorageService {
    
        @Resource
        private StorageMapper storageMapper;
    
        @Override
        public void decrease(Long productId, Integer count) {
            log.info("----->开始减库存");
            log.info("--->productId="+productId+"  count="+count);
            Integer result = storageMapper.decrease(productId, count);
            log.info("---->result="+result);
            log.info("----->结束减库存");
        }
    }
    
  8. 业务类controller:

    import com.xyy.springcloudalibaba.entity.Output;
    import com.xyy.springcloudalibaba.service.StorageService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("storage")
    public class StorageController {
    
        @Autowired
        private StorageService storageService;
    
        //  扣减库存
        @PostMapping("decrease")
        public Output decrease(Long productId, Integer count) {
            storageService.decrease(productId, count);
            return Output.success("扣减库存成功!",null);
        }
    }
    
  9. config : 跟seata-order-service2001相同

  10. 启动类 : SeataStorageMain2002

    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;
    @EnableDiscoveryClient
    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
    public class SeataStorageMain2002 {
        public static void main(String[] args) {
            SpringApplication.run(SeataStorageMain2002.class,args);
        }
    }
    

20.5.2 存款Account-Module

项目结构:
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第48张图片

  1. 创建子项目seata-account-service2003

  2. pom跟seata-order-service2001的pom相同

  3. yml
    除了这些不一样以外,其他一模一样

    server:
      port: 2003
    
    spring:
      application:
        name: seata-account-service
      datasource:
        # 数据库变了`seata_storage`
        url: jdbc:mysql://localhost:3306/seata_account?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
    
  4. file.confregistry.confseata-order-service2001的一模一样

  5. 实体类entity:

    output 实体类点我

    Account

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Account {
    
        private Long id;
    
        /**
         * 用户id
         */
        private Long userId;
    
        /**
         * 总额度
         */
        private BigDecimal total;
    
        /**
         * 已用额度
         */
        private BigDecimal used;
    
        /**
         * 剩余额度
         */
        private BigDecimal residue;
    }
    
  6. mapper :

    @Mapper
    public interface AccountMapper {
    
        // 扣减账户余额
        Integer decrease(@Param("userId") Long userId, @Param("money") BigDecimal money);
    }
    

    xml

    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    
    <mapper namespace="com.xyy.springcloudalibaba.mapper.AccountMapper">
    
        <resultMap id="BaseResultMap" type="com.xyy.springcloudalibaba.entity.Account">
            <id column="id" property="id" jdbcType="BIGINT"/>
            <result column="user_id" property="userId" jdbcType="BIGINT"/>
            <result column="total" property="total" jdbcType="DECIMAL"/>
            <result column="used" property="used" jdbcType="DECIMAL"/>
            <result column="residue" property="residue" jdbcType="DECIMAL"/>
        resultMap>
    
        <update id="decrease">
            UPDATE t_account
            SET residue = residue - #{money},
                used    = used + #{money}
            WHERE user_id = #{userId};
        update>
    mapper>
    
    
  7. 业务类 service:

    AccountService:

    public interface AccountService {
    
        /**
         * 扣减账户余额
         * @param userId 用户id
         * @param money 金额
         */
        void decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
    }
    

    AccountServiceImpl:

    @Service
    @Slf4j
    public class AccountServiceImpl implements AccountService {
        @Resource
        private AccountMapper accountMapper;
    
        /**
         * 扣减账户余额
         */
        @Override
        public void decrease(Long userId, BigDecimal money) {
            log.info("------->account-service中扣减账户余额开始");
            log.info("--->userId:"+userId+"   money:"+money);
            //模拟超时异常(feign默认一秒,没响应就报错),全局事务回滚
            //先注释不搞异常
            //try {
            //    Thread.sleep(30000);
            //} catch (InterruptedException e) {
            //    e.printStackTrace();
            //}
            Integer result = accountMapper.decrease(userId,money);
            log.info("---->result="+result);
            log.info("------->account-service中扣减账户余额结束");
        }
    }
    
  8. 业务类controller:

    @RestController
    @RequestMapping("/account")
    public class AccountController {
    
        @Resource
        private AccountService accountService;
    
        /**
         * 扣减账户余额
         */
        @PostMapping("/decrease")
        public Output decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money){
            accountService.decrease(userId,money);
            return Output.success("扣减账户余额成功!",null);
        }
    }
    
    
  9. config : 跟seata-storage-service2002相同

  10. 启动类 : SeataAccountMain2003

    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;
    
    @EnableDiscoveryClient
    @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
    public class SeataAccountMain2003 {
        public static void main(String[] args) {
            SpringApplication.run(SeataAccountMain2003.class,args);
        }
    }
    

20.6 整体完成测试

  1. 启动服务:
    启动 nacos
    启动 seata
    启动seata-order-service2001 订单服务
    启动seata-storage-service2002 库存服务
    启动seata-account-service2003 账户服务

  2. 访问http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
    用户1,买商品id为1,买10件,一共100元

  3. 结果:
    页面结果:
    在这里插入图片描述
    2001(订单)控制台:
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第49张图片

    数据库:
    在这里插入图片描述

    2002(库存)控制台:
    **加粗样式**

    数据库: 在这里插入图片描述

    2003(账户)控制台: 在这里插入图片描述

    数据库:
    在这里插入图片描述

可以看到数据完美没有出错

20.7 设置异常测试

  1. 将2003连接超时

    将2003的实现类AccountServiceImpl线程休眠打开

  2. 重启2003,再次访问地址测试
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第50张图片
    2001(订单)数据库:订单状态还在进行中: 在这里插入图片描述
    2002(库存)数据库:库存已出
    在这里插入图片描述
    2003(账户)数据库:被扣款(30秒后的结果)
    在这里插入图片描述

可以看到订单还在创建中,并没有结束,假如2003在扣款前抛出异常的话,订单有了,库存已减,就是亏损了啊

20.8 事务解决异常进行回滚

  1. 在所有方法最开始调用的地方加上注解:@GlobalTransactional
    也就是在seata-order-service2001OrderServiceImpl类的create方法上:

    public class OrderServiceImpl implements OrderService {
    	@GlobalTransactional
        public Long create(Order order) {
       		...
        }
    }
    
  2. 重启seata-order-service2001, 这时2001控制台可能会反复一直报错:

    2021-07-09 14:14:19.682 ERROR 4688 — [ileListener_3_1] io.seata.config.FileConfiguration : fileListener execute error, dataId :service.disableGlobalTransaction
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第51张图片
    不用管,这个好像是senta 1.4.2 的bug:官网问答中不止我一个人是这样的

  3. 再次访问: http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100
    SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第52张图片
    还是500,查看数据库:

    2001(订单)数据库: 在这里插入图片描述
    2002(库存)数据库:
    在这里插入图片描述
    2003(账户)数据库:
    在这里插入图片描述

  4. 结果:

    查看2002控制台和2003控制台发现并不是没有执行访问,而是执行访问之后报错,但是数据库并没有订单和库存被扣除的数据,这就是Seata的全局事务注解@GlobalTransactional

20.9 深度总结**

seata 简介:

各种模式介绍:https://seata.io/zh-cn/docs/overview/what-is-seata.html

2019年1月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案

Simple Extensible Autonomous Transaction Architecture,简单可扩展自治事务框架

TC/TM/RM三大组件:
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第53张图片
分布式事务的执行流程

  • TM开启分布式事务(TM向TC注册全局事务记录) ;
  • 按业务场景,编排数据库、服务等事务内资源(RM向TC汇报资源准备状态) ;
  • TM结束分布式事务,事务一阶段结束(TM通知TC提交/回滚分布式事务) ;
  • TC汇总事务信息,决定分布式事务是提交还是回滚;
  • TC通知所有RM提交/回滚资源,事务二阶段结束。

AT模式如何做到对业务的无侵入

  • 基于支持本地 ACID 事务的关系型数据库。

  • Java 应用,通过 JDBC 访问数据库。
    整体机制

    两阶段提交协议的演变:

    • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
    • 二阶段:
      提交异步化,非常快速地完成。
      回滚通过一阶段的回滚日志进行反向补偿。

在一阶段,Seata会拦截“业务SQL”

  1. 解析SQL语义,找到“业务SQL" 要更新的业务数据,在业务数据被更新前,将其保存成"before image”
  2. 执行“业务SQL" 更新业务数据,在业务数据更新之后,
  3. 其保存成"after image”,最后生成行锁。

以上操作全部在一个数据库事务内完成, 这样保证了一阶段操作的原子性。
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第54张图片

二阶段提交

二阶段如果顺利提交的话,因为"业务SQL"在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第55张图片
二阶段回滚

  1. 二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的 “业务SQL",还原业务数据。

  2. 回滚方式便是用"before image"还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和"after image"。

  3. 如果两份数据完全一致就说明没有脏写, 可以还原业务数据,如果不一致就说明有脏写, 出现脏写就需要转人工处理。

SpringCloud( H版 & alibaba )框架开发教程(高级 Alibaba)_第56张图片


你可能感兴趣的:(cloud,java,java,cloud,微服务架构)