SpringCloud Alibaba入门简介

why会出现SpringCloud alibaba

Spring Cloud Netflix项目进入维护模式

SpringCloud alibaba带来了什么

—->>github官网

功能

  • 服务限流降级:默认支持Servlet、Feign、RestTemplate、Dubbo和RocketMQ限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级Metrics监控。
  • 服务注册与发现:适配Spring Cloud服务注册与发现标准,默认集成Ribbon的支持。
  • 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
  • 消息驱动能力:基于Spring Cloud Stream为微服务应用构建消息驱动能力。
  • 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于Cron表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有Worker(schedulerx-client)上执行。

    主要组件

    image.png

    SpringCloud alibaba学习资料获取

  • —->>官网

  • —->>github官网
  • —->>官方参考手册
  • —->>github中文

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

    Nacos简介

    是什么

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

  • Nacos就是注册中心+配置中心的组合,等价于Nacos=Eureka+Config+Bus

    功能

  • 替代Eureka做服务注册中心;

  • 替代Config做服务配置中心;

    下载

  • —->>下载地址

  • https://nacos.io/zh-cn/index.html
  • https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_nacos_discovery

    各种注册中心比较

    image.png

    Nacos安装

  • 直接解压即可(必须有jdk1.8以上的java环境);

  • 单机版启动:./startup.sh -m standalone

    Nacos作为服务注册中心演示

    基于Nacos的服务提供者

    —->>官方教程

    Pom.xml

    1. <dependencies>
    2. <!--SpringCloud ailibaba nacos -->
    3. <dependency>
    4. <groupId>com.alibaba.cloud</groupId>
    5. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    6. </dependency>
    7. <!-- SpringBoot整合Web组件 -->
    8. <dependency>
    9. <groupId>org.springframework.boot</groupId>
    10. <artifactId>spring-boot-starter-web</artifactId>
    11. </dependency>
    12. <dependency>
    13. <groupId>org.springframework.boot</groupId>
    14. <artifactId>spring-boot-starter-actuator</artifactId>
    15. </dependency>
    16. <!--日常通用jar包配置-->
    17. <dependency>
    18. <groupId>org.springframework.boot</groupId>
    19. <artifactId>spring-boot-devtools</artifactId>
    20. <scope>runtime</scope>
    21. <optional>true</optional>
    22. </dependency>
    23. <dependency>
    24. <groupId>org.projectlombok</groupId>
    25. <artifactId>lombok</artifactId>
    26. <optional>true</optional>
    27. </dependency>
    28. <dependency>
    29. <groupId>org.springframework.boot</groupId>
    30. <artifactId>spring-boot-starter-test</artifactId>
    31. <scope>test</scope>
    32. </dependency>
    33. </dependencies>

    application.yaml

    1. server:
    2. port: 9001
    3. spring:
    4. application:
    5. name: nacos-payment-provider
    6. cloud:
    7. nacos: #注册进nacos
    8. discovery:
    9. server-addr: 124.70.84.192:8848
    10. #配置Nacos地址(千万不要写带上"http://")
    11. management:
    12. endpoints:
    13. web:
    14. exposure:
    15. include: '*'

    主启动类

    @EnableDiscoveryClient
    @SpringBootApplication
    public class PaymentMain9001 {
      public static void main(String[] args) {
          SpringApplication.run(PaymentMain9001.class,args);
      }
    }
    

    业务类

    @RestController
    public class PaymentController {
      @Value("${server.port}")
      private String port;
      @GetMapping(value = "/payment/nacos/{id}")
      public String getPayment(@PathVariable("id") Integer id){
          return "nacos registry, serverPort: "+ port+"\t id"+id;
      }
    }
    

    测试

    image.png

    基于Nacos的服务消费者

    pom.xml

    <dependencies>
    <!--SpringCloud ailibaba nacos -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
    <dependency>
      <groupId>com.atguigu</groupId>
      <artifactId>cloud-api-commons</artifactId>
      <version>${project.version}</version>
    </dependency>
    <!-- SpringBoot整合Web组件 -->
    <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>
    <!--日常通用jar包配置-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    </dependencies>
    

    application.yaml

    server:
    port: 83
    spring:
    application:
      name: nacos-order-consumer
    cloud:
      nacos:
        discovery:
          server-addr: 124.70.84.192:8848
    #消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
    service-url: #非必须,只是为了后面方便引用地址
    nacos-user-service: http://nacos-payment-provider
    

    nacos自带Ribbon

    image.png

    主启动类

    @EnableDiscoveryClient
    @SpringBootApplication
    public class OrderNacosMain83 {
      public static void main(String[] args) {
          SpringApplication.run(OrderNacosMain83.class,args);
      }
    }
    

    业务类

    config
    @Configuration
    public class ApplicationContextBean {
      @Bean
      @LoadBalanced
      public RestTemplate getRestTemplate() {
          return new RestTemplate();
      }
    }
    

    controller
    @RestController
    public class OrderNacosController {
      @Autowired
      private RestTemplate restTemplate;
      @Value("${service-url.nacos-user-service}")
      private String serverURL;
      @GetMapping("/consumer/payment/nacos/{id}")
      public String paymentInfo(@PathVariable("id") Long id) {
          return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class);
      }
    }
    

    测试

    image.png

    服务注册中心对比

    Nacos全景图

    image.png

    Nacos和CAP

    image.png
    nacos支持AP模式CP模式之间的转换;

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

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

AP模式转换为CP模式:

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

    Nacos作为服务配置中心演示

    这里演示windows版本nacos,因为linux版本需要先配数据库才能创建配置;

    Nacos作为配置中心-基础配置

    pom.xml

    <dependencies>
    <!--nacos-config-->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <!--nacos-discovery-->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--web + actuator-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--一般基础配置-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    </dependencies>
    

    两个yaml配置文件

    why配置两个
  • Nacosspringcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动。

  • springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application;

    bootstrap.yaml
    # 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格式的配置
    

    application.yaml
    spring:
    profiles:
      active: dev #表示开发环境
    

    主启动类

    @EnableDiscoveryClient
    @SpringBootApplication
    public class NacosConfigClientMain3377 {
      public static void main(String[] args) {
          SpringApplication.run(NacosConfigClientMain3377.class, args);
      }
    }
    

    业务类

    @RestController
    @RefreshScope //在控制器类加入@RefreshScope注解使当前类下的配置支持Nacos的动态刷新功能。
    public class ConfigClientController {
      @Value("${config.info}")
      private String configInfo;
      @GetMapping("/config/info")
      public String getConfigInfo() {
          return configInfo;
      }
    }
    

    在Nacos中添加配置信息

    Nacos中的dataid的组成格式及与SpringBoot配置文件中的匹配规则(官网):
    ${prefix}**-**${spring.profiles.active}.${file-extension}

  • **prefix**默认为spring.application.name的值,也可以通过配置项spring.cloud.nacos.config.prefix来配置。

  • **spring.profiles.active**即为当前环境对应的profile,详情可以参考Spring Boot文档或者springboot2笔记注意:当 spring.profiles.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 **${prefix}.${file-extension}**
  • **file-exetension** 为配置内容的数据格式,可以通过配置项spring.cloud.nacos.config.file-extension来配置。目前只支持properties和yaml类型。

image.png

总结

image.png

Nacos作为配置中心-分类配置

多环境多项目管理

问题1:
实际开发中,通常一个系统会准备:dev开发环境、test测试环境、prod生产环境。如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢?
问题2:
一个大型分布式微服务系统会有很多微服务子项目,每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境……那怎么对这些微服务配置进行管理呢?

Nacos的图形化管理界面

配置管理

image.png

命名空间

image.png

Namespace+Group+Data ID三者关系

1、是什么
类似Java里面的package名和类名,最外层的namespace是可以用于区分部署环境的,Group和DataID逻辑上区分两个目标对象。
2、三者情况:
image.png
默认情况:Namespace=publicGroup=DEFAULT_GROUP默认Cluster是DEFAULT

  • Nacos默认的命名空间是public,Namespace主要用来实现隔离。比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个Namespace,不同的Namespace之间是隔离的。
  • Group默认是DEFAULT_GROUP,Group可以把不同的微服务划分到同一个分组里面去;
  • Service就是微服务;一个Service可以包含多个Cluster(集群),Nacos默认Cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。
    • 比方说为了容灾,将Service微服务分别部署在了杭州机房和广州机房,这时就可以给杭州机房的Service微服务起一个集群名称(HZ),给广州机房的Service微服务起一个集群名称(GZ),还可以尽量让同一个机房的微服务互相调用,以提升性能。
  • 最后是Instance,就是微服务的实例。

    三种方案加载配置

    DataID方案

    直接修改application.yaml中的此处即可:

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

    Group方案

    1、在不同的分组里创建同一份配置文件spring.profiles.active=info
    image.png
    2、在配置文件中的spring.cloud.nacos.config下面设置一个属性**group**即可:

    spring:
    application:
      name: nacos-config-client
    cloud:
      nacos:
        discovery:
          server-addr: localhost:8848 #Nacos服务注册中心地址
        config:
          server-addr: localhost:8848 #Nacos作为配置中心地址
          file-extension: yaml #指定yaml格式的配置
          group: TEST_GROUP #在这里设置分组**********这里*****
          #group: DEV_GROUP
    

    Namespace方案

    1、新建dev/testNamespace
    image.png
    2、之后就可以在服务列表里面选择命名空间来创建配置了;
    3、yaml文件配置该命名空间ID即可(配置开发环境的):

    #********************application.yaml
    spring:
    application:
      name: nacos-config-client
    cloud:
      nacos:
        discovery:
          server-addr: localhost:8848 #Nacos服务注册中心地址
        config:
          server-addr: localhost:8848 #Nacos作为配置中心地址
          file-extension: yaml #指定yaml格式的配置
          #group: TEST_GROUP #在这里设置分组
          group: DEV_GROUP
          namespace: 96af6283-d9f9-4b18-ab2e-ed814a9b95e6
          #配置命名空间ID**********************这里配置
    #********************bootstrap.yaml
    spring:
    profiles:
      #active: test #表示测试环境
      active: dev #表示开发环境
      #active: info
    

    Nacos集群和持久化配置(重要)

    官网说明

    —->>nacos集群官网说明
    image.png
    按照上述,我们需要mysql数据库;

    Nacos部署环境

    Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,强烈不建议部署在公共网络环境。

    Nacos支持三种部署模式

  • 单机模式 - 用于测试和单机试用。

  • 集群模式 - 用于生产环境,确保高可用。
  • 多集群模式 - 用于多数据中心场景。
  • —->>官网教程

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

  • 1.安装数据库,版本要求:5.6.5+
  • 2.初始化mysql数据库,数据库初始化文件:nacos-mysql.sql
  • 3.修改conf/application.properties文件,增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url用户名密码

    Nacos持久化配置解释

    Nacos默认自带的是嵌入式数据库derby(—->>github官网源码配置文件),这样多台nacos就有多个单独的内嵌数据库,那么数据的一致性就无法保证,因此真实的生产环境不应该用内嵌的数据库,我们用mysql数据库;

    derby到mysql切换配置步骤

    1、nacos-server-1.1.4\nacos\conf目录下找到sql脚本nacos-mysql.sql,在自己的数据库下执行这个脚本;
    2、nacos-server-1.1.4\nacos\conf目录下找到application.properties进行配置:
    spring.datasource.platform=mysql
    db.num=1
    db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
    db.user=root
    db.password=12345ssdlh
    
    3、之后重启nacos并登录nacos管理页面,然后创建一个配置,可以在config_info表中查到这条配置的信息即证配置成功,如下:
    image.png

    Linux版Nacos+MySQL生产环境配置

    1个Nginx**+**3个nacos注册中心(版本1.1.4)**+**1个mysql
    1、首先进行持久化(配置mysql)配置,如上;
    2、Linux服务器上nacos的集群配置cluster.conf:
    先将cluster.conf.example拷贝一份为cluster.conf,为了安全:

    [root@ecs-mcr conf]# pwd /opt/module/nacos/conf [root@ecs-mcr conf]# cp cluster.conf.example cluster.conf

vim编辑cluster.conf里面的内容(把原来的注掉):

124.70.84.192:5555 124.70.84.192:3333 124.70.84.192:7777

这个IP不能写127.0.0.1;
3、编辑Nacos的启动脚本**startup.sh**,使它能够接受不同的启动端口:
首先startup.sh备份一份startup.sh.example,为了安全:

[root@ecs-mcr bin]# pwd /opt/module/nacos/bin [root@ecs-mcr bin]# cp startup.sh startup.sh.example

vim配置startup.sh里面的内容:
image.png
并在nacos的**application.properties**中添加配置以下内容,设置固定IP
nacos.inetutils.ip-address=124.70.84.192(服务器ip)
4、测试nacos伪集群:

[root@ecs-mcr bin]# ./startup.sh -m cluster -p 7777 [root@ecs-mcr bin]# ./startup.sh -m cluster -p 3333 [root@ecs-mcr bin]# ./startup.sh -m cluster -p 5555

看看是否开启够了三台:

[root@ecs-mcr bin]# ps -ef|grep nacos|grep -v grep|wc -l 3

5、Nginx的配置,由它作为负载均衡器:
修改nginx的配置文件nginx.conf

....   
    upstream cluster{
        server 127.0.0.1:5555;
        server 127.0.0.1:3333;
        server 127.0.0.1:7777;
        }
#虚拟主机1
    server {
        listen       80;
        server_name  localhost;


        location / {
            #proxy_pass http://192.168.80.132/;
            #root   /home/www/www;
            #index  index.html index.htm;
            proxy_pass http://cluster; #最后有个分号,千万别忘
        }
....

6、测试**nginx方向代理+nacos集群**
地址:http://124.70.84.192/nacos,可以访问到nacos:
image.png
至此,nginx+nacos集群+mysql配置成功

nginx+nacos集群+mysql测试

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos: #注册进nacos
      discovery:
        server-addr: 124.70.84.192:80 
        #配置Nacos集群的nginx反向代理地址
        #即便是80端口也要写端口号,不写端口号默认转到8848端口

结果服务成功注册进nacos;

SpringCloud Alibaba Sentinel实现熔断与限流

Sentinel简介

image.png
就是Hystrix的升级版;
Sentinel主要特征:
image.png

安装

  • 下载地址:[https://github.com/alibaba/Sentinel/releases](https://github.com/alibaba/Sentinel/releases)
  • 官网使用教程:[--->>官网教程](https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel)
  • 运行,是个jar包,直接:java -jar sentinel-dashboard-1.7.0.jar
  • 访问sentinel管理界面:http://ip号:8080
  • 默认登录账号密码均为**sentinel**

    Sentinel分为两个部分:

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

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

    初始化演示工程

  • 本次演示sentinel1.7.0版本;

  • 首先保证nacos成功配置并运行;

    服务模块

    pom.xml

    <dependencies>
    <!--SpringCloud ailibaba nacos -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--SpringCloud ailibaba sentinel-datasource-nacos后续做持久化用到-->
    <dependency>
      <groupId>com.alibaba.csp</groupId>
      <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    <!--SpringCloud ailibaba sentinel -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <!--openfeign-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!-- SpringBoot整合Web组件+actuator -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--日常通用jar包配置-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>4.6.3</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    </dependencies>
    

    application.yaml

    server:
    port: 8401
    spring:
    application:
      name: cloudalibaba-sentinel-service
    cloud:
      nacos:
        discovery:
          #Nacos服务注册中心地址
          server-addr: 124.70.84.192:80
      sentinel:
        transport:
          #配置Sentinel dashboard地址
          dashboard: localhost:8080
          #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
          port: 8719
    management:
    endpoints:
      web:
        exposure:
          include: '*'
    

    主启动类

    @EnableDiscoveryClient
    @SpringBootApplication
    public class MainApp8401 {
      public static void main(String[] args) {
          SpringApplication.run(MainApp8401.class, args);
      }
    }
    

    业务类

    @RestController
    public class FlowLimitController {
      @GetMapping(value = "/test1")
      public String test1(){return "test1";}
      @GetMapping(value = "/test2")
      public String test2(){return "test2";}
    }
    

    测试

    先启动nacos,再启动sentinel,最后启动这个服务模块;
    由于Sentinel采用的懒加载说明,我们执行一次访问即可http://localhost:8401/test2
    image.png

    流控规则

    介绍

    image.png
    image.png

    流控模式

    直接(默认)

    QPS与线程数的区别:

  • 比如去一家银行办理业务;

  • QPS就是每秒钟大于阈值的人(请求)去敲银行大门要办理业务,此时直接报错(此时还在银行门外);
  • 线程数就是(假如阈值是1)每秒钟只有1个人(线程)能进到银行里面办理业务,大于一个进入直接报错;

    现象

    image.png

    思想

    直接调用默认报错信息,技术方面OK。but,是否应该有我们自己的后续处理?类似豪猪哥有个fallback的兜底方法?后面讲;

    关联

    当关联的资源达到阈值时,就限流自己【相当于别人惹事自己买单】
    image.png

    链路

    多个请求调用了同一个微服务;

    流控效果

    预热(Warm Up)

    —->>官网介绍

    说明

    公式:阈值除以coldFactor(冷加载因子,默认值为3),经过预热时长后才会达到阈值;

  • 当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。

源码证明冷加载因子默认值为3

  • com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
  • image.png

    配置说明

    image.png

    排队等待

    匀速排队,阈值必须设置为QPS,—->>官方描述
    image.png
    源码所在类:

  • com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController

    降级规则

    —->>官网介绍(官网针对的是Sentinel 1.8.0 及以上版本。1.8.0 版本对熔断降级特性进行了全新的改进升级);

  • Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例:调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响别的资源而导致级联错误。

  • 当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。
  • Sentinel的断路器是没有半开状态的;

image.png
RT(平均响应时间,秒级)

  • 平均响应时间超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级,窗口期过后关闭断路器;
  • RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效);

异常比列(秒级)

  • QPS>=5异常比例(秒级统计)超过阈值时触发降级;时间窗口结束后,关闭降级;

异常数(分钟级)

  • 异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级;

    降级策略实战

    RT

    image.png
    image.png
    image.png

    Sentinel1.8.0版本(补)

    1.8.0 版本对熔断降级特性进行了全新的改进升级,RT又平均响应时间变成了慢调用比例,如下:

  • 官网原文:慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用RT则结束熔断,若大于设置的慢调用RT则会再次被熔断。

  • 也就是说1.8.0版本有半开状态(也就是探测恢复状态(HALF-OPEN 状态))了;

    异常比例

    image.png
    image.png

    Sentinel1.8.0版本(补)

    image.png

    异常数

    image.png
    时间窗口一定要大于等于60秒。
    image.png

    Sentinel1.8.0版本(补)

    image.png

    热点key限流

    介绍

    —->>官网介绍
    何为热点,热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作;
    image.png

    示例

    兜底方法分为系统默认和客户自定义两种:

  • 之前的case,限流出问题后,都是用sentinel系统默认的提示:Blocked by Sentinel (flow limiting)

  • 使用@SentinelResource来进行自定义(HystrixCommand到@SentinelResource);

    controller

    @GetMapping(value = "/testHotkey")
    @SentinelResource(value = "testHotkey",//这里value随便写
                   blockHandler = "deal_testHotkey"
                   //如果某个资源违背了sentinel配的热点规则,就交由blockHandler指定的方法处理
                  )
    public String testHotkey(//两个参数p1,p2都是可传可不传
     @RequestParam(value = "p1",required = false)String p1,
     @RequestParam(value = "p1",required = false)String p2){
     return "*-*-*-*-*-**-*testHotkey()";
    }
    public String deal_testHotkey(String p1, String p2, BlockException e){
     return "deal_testHotkey-----------/(ㄒoㄒ)/~~";
    }
    

    sentinel配置

    image.png
    @SentinelResource注解必须配置blockHandler参数,不然触发热点规则之后用户页面直接呈现不友好的error page页面;

    参数例外项配置

    上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流,这是普通情况,那如果我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样呢?假如当p1的值等于5时,它的阈值可以达到200;
    image.png

    总结

    @SentinelResource处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;运行出错该走异常走异常,@SentinelResource不管;

    系统规则

    —->>官网介绍
    Sentinel系统自适应限流从整体维度对应用入口流量进行控制,结合应用的LoadCPU使用率总体平均RT入口QPS并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
    image.png

  • 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达到阈值即触发系统保护。

    @SentinelResource案例

    Controller

    新增控制层RateLimitController

    @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 服务不可用");
      }
    }
    

    注意:@SentinelResource注解不支持private()方法;

    限流(根据资源名/URL地址)+后续处理

    image.png

    上面兜底方案面临的问题

  • 系统默认的,没有体现我们自己的业务要求。

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

    客户自定义限流处理逻辑

    创建公共兜底类CustomerBlockHandler用于自定义限流处理逻辑

    公共兜底类CustomerBlockHandler

    public class CustomerBlockHandler {
      public static CommonResult handleException(BlockException exception){
          return new CommonResult(40404,"自定义的限流处理信息......CustomerBlockHandler");
      }
    }
    

    使用此公共兜底类

    @GetMapping("/byResource/one")
    @SentinelResource(value = "byResource1",
                    blockHandlerClass = CustomerBlockHandler.class,//兜底方法所在类
                    blockHandler = "handleException")//所用的兜底类的兜底方法
    public CommonResult byResource1(){
      return new CommonResult(200,"按资源名称限流测试OK",
                              new Payment(2020L,"serial001"));
    }
    

    更多注解属性说明

    Sentinel主要有三个核心Api:

  • SphU定义资源;

  • Tracer定义统计;
  • ContextUtil定义了上下文;

    服务熔断功能

    **sentinel**整合ribbon+openFeign+fallback
    启动nacos和sentinel;

    服务提供者9003/9004

    新建cloudalibaba-provider-payment9003/9004两个一样,只有端口号不同,以9003为例;

    pom.xml

    <dependencies>
    <!--SpringCloud ailibaba nacos -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
      <groupId>com.atguigu</groupId>
      <artifactId>cloud-api-commons</artifactId>
      <version>${project.version}</version>
    </dependency>
    <!-- SpringBoot整合Web组件 -->
    <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>
    <!--日常通用jar包配置-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    </dependencies>
    

    application.yaml

    server:
    port: 9003
    spring:
    application:
      name: nacos-payment-provider
    cloud:
      nacos:
        discovery:
          server-addr: 124.70.84.192:80 #配置Nacos地址
    management:
    endpoints:
      web:
        exposure:
          include: '*'
    

    主启动类

    @SpringBootApplication
    @EnableDiscoveryClient
    

    控制层

    @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 CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
          Payment payment = hashMap.get(id);
          CommonResult<Payment> result = 
              new CommonResult(200,"from mysql,serverPort:  "+serverPort,payment);
          return result;
      }
    }
    

    服务消费者84

    pom.xml

    <dependencies>
    <!--SpringCloud ailibaba nacos -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--SpringCloud ailibaba sentinel -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
    <dependency>
      <groupId>com.atguigu</groupId>
      <artifactId>cloud-api-commons</artifactId>
      <version>${project.version}</version>
    </dependency>
    <!-- SpringBoot整合Web组件 -->
    <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>
    <!--日常通用jar包配置-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    </dependencies>
    

    application.yaml

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

    主启动类

    @EnableDiscoveryClient
    @SpringBootApplication
    

    业务类

    配置类
    @Configuration
    public class ApplicationContextConfig {
      @Bean
      @LoadBalanced
      public RestTemplate getRestTemplate() {
          return new RestTemplate();
      }
    }
    

    控制层
    @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",
              fallback="handlerFallback",//fallback配置出现异常后的兜底方法⭐
              blockHandler = "blockHandler")//blockHandler只负责sentinel里配置的降级限流⭐
      public CommonResult<Payment> fallback(@PathVariable Long id) {
          CommonResult<Payment> result = 
              restTemplate.getForObject(SERVICE_URL+"/paymentSQL/"+id,
                                        CommonResult.class,id);
          if (id>=4||id<1) {
              throw new IllegalArgumentException ("IllegalArgumentException,
                                                  非法参数异常....");
          }else if (result.getData() == null) {
              throw new NullPointerException ("NullPointerException,
                                              该ID没有对应记录,空指针异常");
          }
          return result;
      }
      public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
          Payment payment = new Payment(id,"null");
          return new CommonResult<>(444,
                  "兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
      }
      public CommonResult blockHandler(@PathVariable  Long id, 
                                       BlockException blockException) {
          Payment payment = new Payment(id,"null");
          return new CommonResult<>(445,
                  "无此流水: blockException  "+blockException.getMessage(),payment);
      }
    }
    

    总结

    blockHandlerfallback都进行了配置并都出发了两者的规则时,则被限流降级而抛出BlockException时只会进入blockHandler处理逻辑。

    忽略属性(补充)

    **@SentinelResource**注解里的属性exceptionsToIgnore={IllegalArgumentException.class}(以IllegalArgumentException异常为例);
    image.png

    使用OpenFeign

    pom.xml新增OpenFeign的依赖

    <!--SpringCloud openfeign -->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    

    激活Sentinel对Feign的支持

    application.yaml中增加激活SentinelFeign的支持;

    #激活Sentinel对Feign的支持
    feign:
    sentinel:
      enabled: true
    

    主启动类新增@EnableFeignClients

    @EnableDiscoveryClient
    @SpringBootApplication
    @EnableFeignClients//⭐⭐新增⭐⭐
    

    业务类

    Service
    //⭐⭐⭐⭐⭐⭐接口⭐⭐⭐⭐⭐⭐
    @FeignClient(value = "nacos-payment-provider",
          fallback=PaymentFallbackService.class)//调用中关闭9003服务提供者
    public interface PaymentService {
      @GetMapping(value = "/paymentSQL/{id}")
      public CommonResult<Payment> paymentSQL(@PathVariable("id")Long id);
    }
    //⭐⭐⭐接口实现类兼兜底方法⭐⭐⭐
    @Service
    public class PaymentFallbackService implements PaymentService {
      @Override
      public CommonResult<Payment> paymentSQL(Long id) {
          return new CommonResult<>(444,"服务降级返回",new Payment(id, "errorSerial.."));
      }
    }
    

    controller
    //⭐⭐⭐⭐⭐⭐OpenFeign⭐⭐⭐⭐⭐⭐
    @Resource
    private PaymentService paymentService;
    @GetMapping(value = "/consumer/openfeign/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
      if(id == 4) {throw new RuntimeException("没有该id");}
      return paymentService.paymentSQL(id);
    }
    

    测试

    首先正常访问,可以:
    image.png
    把服务提供者9004和9003关掉再次访问:
    image.png

    熔断框架比较

    image.png

    规则持久化

    是什么

    一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化。将限流配置规则持久化进Nacos保存,只要刷新服务消费者某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对服务消费者上sentinel上的流控规则持续有效;

    示例

    对初始化演示工程的8401进行改造;

    pom.xml

    <!--SpringCloud ailibaba sentinel-datasource-nacos后续做持久化用到-->
    <dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    

    application.yaml

    spring:
    application:
      name: cloudalibaba-sentinel-service
    cloud:
      nacos:
        discovery:
          #Nacos服务注册中心地址
          server-addr: 124.70.84.192:80
      sentinel:
        transport:
          #配置Sentinel dashboard地址
          dashboard: localhost:8080
          #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
          port: 8719
        datasource: #⭐⭐⭐添加数据源配置⭐⭐⭐
          ds1:
            nacos:
              server-addr: ${spring.cloud.nacos.discovery.server-addr}
              dataId: ${spring.application.name}
              groupId: DEFAULT_GROUP
              data-type: json
              rule-type: flow
    

    nacos控制台新增配置

    image.png

  • resource:资源名称;

  • limitApp:来源应用;
  • grade:阈值类型,0表示线程数,1表示QPS;
  • count:单机阈值;
  • strategy:流控模式,0表示直接,1表示关联,2表示链路;
  • controlBehavior:流控效果,0表示快速失败,1表示Warm Up预热,2表示排队等待;
  • clusterMode:是否集群。

    测试

    之后在模块重启时这个在nacos里创建的配置就会自动转化为sentinel里面的流控规则,并且一直存在,就有持久性;

    总结

    image.png

    SpringCloud Alibaba Seata处理分布式事务

    Seata简介

    —->>官网
    Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
    分布式事务处理过程的**1ID**+**3组件**模型:

  • id-**Transaction ID XID**:全局唯一的事务ID;

  • 组件-**Transaction Coordinator (TC)**事务协调者,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
  • 组件-**Transaction Manager (TM)**事务管理器,控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
  • 组件-**Resource Manager (RM)**资源管理器,控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚;

    分布式事务过程

    image.png
  1. TMTC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID
  2. XID在微服务调用链路的上下文中传播;
  3. RMTC注册分支事务,将其纳入XID对应全局事务的管辖;
  4. TMTC发起针对XID的全局提交或回滚决议;
  5. TC调度XID下管辖的全部分支事务完成提交或回滚请求;

    下载安装

    —->>下载地址;下载之后解压;

  6. 修改配置文件seata/conf/file.conf,首先将file.conf备份一份以防改错;

  7. 修改file.conf内的内容,主要修改:自定义事务组名称**+**事务日志存储模式为db**+**数据库连接信息
    1. image.png
  8. mysql5.7数据库新建库seata,进入该库并导入seata准备的sql文件(1.0版本之后,这个sql文件不再和服务端压缩一块,而是要单独下载其对应版本的source压缩包里面:seata-1.0.0/script/server/db/mysql.sql),导入成功如下:
    1. image.png
  9. 修改seata/conf目录下的registry.conf配置文件:
    1. image.png
  10. 启动:./seata-server.sh

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

    以下演示都需要先启动Nacos后启动Seata,保证两个都OK

    分布式事务业务说明

  • 这里我们会创建三个服务,一个订单服务,一个库存服务,一个账户服务。
  • 当用户下单时,会在订单服务中创建一个订单然后通过远程调用库存服务来扣减下单商品的库存再通过远程调用账户服务来扣减用户账户里面的余额最后在订单服务中修改订单状态为已完成
  • 该操作跨越三个数据库,有两次远程调用,很明显会有分布式事务问题。
  • 下订单—->扣库存—->减账户(余额)

    创建相应的库及表

  1. 创建业务数据库:
    1. seata_order:存储订单的数据库;
    2. seata_storage:存储库存的数据库;
    3. seata_account:存储账户信息的数据库。
  2. 按照上述3库分别建对应业务表:
    1. seata_order库下建t_order表;
    2. seata_storage库下建t_storage表;
    3. seata_account库下建t_account表;
  3. 按照上述3库分别建对应的回滚日志表(同样在source压缩文件中,seata-1.0.0/script/client/at/db/mysql.sql),这个sql文件在上面三个库中每个执行一遍;
  4. 最终效果:
    1. image.png

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

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

      新建订单Order-Module

      pom.xml

      <dependencies>
      <!--nacos-->
      <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
      </dependency>
      <!--seata-->
      <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>
      <!--feign-->
      <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
      </dependency>
      <!--web-actuator-->
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      <!--mysql-druid-->
      <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.37</version>
      </dependency>
      <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.1.10</version>
      </dependency>
      <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.0.0</version>
      </dependency>
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      </dependency>
      <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
      </dependency>
      </dependencies>
      

      application.yaml

      server:
      port: 2001
      spring:
      application:
      name: seata-order-service
      cloud:
      alibaba:
      seata:
        #自定义事务组名称需要与seata-server中的对应
        tx-service-group: fsp_tx_group
      nacos:
      discovery:
        server-addr: localhost:8848
      datasource:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://124.80.74.192:3306/seata_order
      username: root
      password: 12345ssdlh
      feign:
      hystrix:
      enabled: false
      logging:
      level:
      io:
      seata: info
      mybatis:
      mapperLocations: classpath:mapper/*.xml
      

      fire.conf

      0.9.0是直接复制seara里面fire.conf的内容,1.0.0版本是复制fire.conf.example的内容,之后做一些自己数据库的配置;
      transport {
      type = "TCP"
      server = "NIO"
      heartbeat = true
      thread-factory {
      boss-thread-prefix = "NettyBoss"
      worker-thread-prefix = "NettyServerNIOWorker"
      server-executor-thread-prefix = "NettyServerBizHandler"
      share-boss-worker = false
      client-selector-thread-prefix = "NettyClientSelector"
      client-selector-thread-size = 1
      client-worker-thread-prefix = "NettyClientWorkerThread"
      boss-thread-size = 1
      worker-thread-size = 8
      }
      shutdown {
      wait = 3
      }
      serialization = "seata"
      compressor = "none"
      }
      service {
      vgroup_mapping.fsp_tx_group = "default" #修改自定义事务组名称
      default.grouplist = "127.0.0.1:8091"
      enableDegrade = false
      disable = false
      max.commit.retry.timeout = "-1"
      max.rollback.retry.timeout = "-1"
      disableGlobalTransaction = false
      }
      client {
      async.commit.buffer.limit = 10000
      lock {
      retry.internal = 10
      retry.times = 30
      }
      report.retry.count = 5
      tm.commit.retry.count = 1
      tm.rollback.retry.count = 1
      }
      store {
      mode = "db"
      file {
      dir = "sessionStore"
      max-branch-session-size = 16384
      max-global-session-size = 512
      file-write-buffer-cache-size = 16384
      session.reload.read_size = 100
      flush-disk-mode = async
      }
      db {
      datasource = "dbcp"
      db-type = "mysql"
      driver-class-name = "com.mysql.jdbc.Driver"
      url = "jdbc:mysql://124.70.84.192:3306/seata"
      user = "root"
      password = "12345ssdlh"
      min-conn = 1
      max-conn = 3
      global.table = "global_table"
      branch.table = "branch_table"
      lock-table = "lock_table"
      query-limit = 100
      }
      }
      lock {
      mode = "remote"
      local {
      }
      remote {
      }
      }
      recovery {
      committing-retry-period = 1000
      asyn-committing-retry-period = 1000
      rollbacking-retry-period = 1000
      timeout-retry-period = 1000
      }
      transaction {
      undo.data.validation = true
      undo.log.serialization = "jackson"
      undo.log.save.days = 7
      undo.log.delete.period = 86400000
      undo.log.table = "undo_log"
      }
      metrics {
      enabled = false
      registry-type = "compact"
      exporter-list = "prometheus"
      exporter-prometheus-port = 9898
      }
      support {
      spring {
      datasource.autoproxy = false
      }
      }
      

      registry.conf

      seata里面的registry.conf里面的内容;
      registry {
      type = "nacos"
      nacos {
      serverAddr = "124.70.84.192:80"
      namespace = ""
      cluster = "default"
      }
      eureka {
      serviceUrl = "http://localhost:8761/eureka"
      application = "default"
      weight = "1"
      }
      redis {
      serverAddr = "localhost:6379"
      db = "0"
      }
      zk {
      cluster = "default"
      serverAddr = "127.0.0.1:2181"
      session.timeout = 6000
      connect.timeout = 2000
      }
      consul {
      cluster = "default"
      serverAddr = "127.0.0.1:8500"
      }
      etcd3 {
      cluster = "default"
      serverAddr = "http://localhost:2379"
      }
      sofa {
      serverAddr = "127.0.0.1:9603"
      application = "default"
      region = "DEFAULT_ZONE"
      datacenter = "DefaultDataCenter"
      cluster = "default"
      group = "SEATA_GROUP"
      addressWaitTime = "3000"
      }
      file {
      name = "file.conf"
      }
      }
      config {
      type = "file"
      nacos {
      serverAddr = "localhost"
      namespace = ""
      }
      consul {
      serverAddr = "127.0.0.1:8500"
      }
      apollo {
      app.id = "seata-server"
      apollo.meta = "http://192.168.1.204:8801"
      }
      zk {
      serverAddr = "127.0.0.1:2181"
      session.timeout = 6000
      connect.timeout = 2000
      }
      etcd3 {
      serverAddr = "http://localhost:2379"
      }
      file {
      name = "file.conf"
      }
      }
      

      domain

      就是javaBean类;
      CommonResult
      @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);
      }
      }
      
      Order
      @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:已完结
      }
      

      Dao接口及实现

      接口OrderDao
      @Mapper
      public interface OrderDao {
      //1.新建订单
      void create(Order order);
      //2.修改订单状态(0->1)
      void update(@Param("userId")Long userId,@Param("status")Integer status);
      }
      
      映射文件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.cloudalibaba.dao.OrderDao">
      <resultMap id="BaseResultMap" type="com.atguigu.cloudalibaba.domain.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>
      <!--void create(Order order);-->
      <insert id="create">
        insert into t_order(id,user_id,product_id,count,money,status)
        values (null,#{userId},#{productId},#{count},#{money},0);
      </insert>
      <!--void update(@Param("userId")Long userId,@Param("status")Integer status);-->
      <update id="update">
        update t_order set status = 1
        where user_id=#{userId and status = #{status};
      </update>
      </mapper>
      

      Service接口及实现

      版本变更太大,例子暂无法实现,参照官网:http://seata.io/zh-cn/

      使用

      在service业务层类上面加上注解:
      @GlobalTransactional(name = "create-order",rollbackFor = Exception.class)
  • name是事务的名字,可以随意起,保持唯一性;
  • rollbackFor = Exception.class表示发生任何异常都进行回滚;

    seata原理

    image.png

    分布式事务的执行流程

  • TM开启分布式事务(TMTC注册全局事务记录);

  • 按业务场景,编排数据库、服务等事务内资源(RMTC汇报资源准备状态 );
  • TM结束分布式事务,事务一阶段结束(TM通知TC提交/回滚分布式事务);
    • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
  • TC汇总事务信息,决定分布式事务是提交还是回滚;
  • TC通知所有RM提交/回滚 资源,事务二阶段结束。
    • 二阶段:
      • 提交异步化,非常快速地完成。
      • 回滚通过一阶段的回滚日志进行反向补偿。

        AT模式(默认)

        如何做到对业务的无侵入?

        一阶段加载

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

以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。
image.png

二阶段提交

二阶段如是顺利提交的话:因为“业务 SQL”在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
image.png

二阶段回滚

二阶段回滚:

  • 二阶段如果是回滚的话,Seata就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。
  • 回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和“after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

image.png

示例流程

image.png