作者:🍧dabing(王甜甜)

视频:尚硅谷 2020 周阳 SpringCloud(H 版 & alibaba)

官方文档:https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#features

github 上代码地址:https://github.com/dabing85/springcloud2020

gitee 上代码地址:https://gitee.com/hedabing/springcloud2020

笔记来自视频,只不过再加些自己练习过程的笔记。

# 十八、SpringCloud Alibaba Sentinel 实现熔断与限流

这个跟 Hystrix 断路器差不多,不过这个多了一些规则,Hystrix 是处理异常比例达到一定阈值时进行服务降级,而这个 Sentinel 会有更多规则,异常比较、慢调用比例、异常数的规则,还有一些限流的规则,还有自己的控制台可视化比较好操作等等。第 9 点有对比列表。

# 1 - Sentinel 介绍

# 1.1. 官网

https://github.com/alibaba/Sentinel

中文

https://github.com/alibaba/Sentinel/wiki/ 介绍

# 1.2. 是什么

一句话解释,之前我们讲解过的 Hystrix

img

img

# 1.3. 能干嘛

img

img

# 1.4. 去哪下

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

img

# 1.5. 怎么玩

https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html#_spring_cloud_alibaba_sentinel

img

  • 服务使用中的各种问题

    • 服务雪崩

    • 服务降级

    • 服务熔断

    • 服务限流

# 2 - 安装 Sentinel 控制台

# 2.1. sentinel 组件由 2 部分组成

Sentinel 分为两个部分:

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

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

  • 后台

  • 前台 8080

# 2.2. 安装步骤

# 1. 下载

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

下载到本地 sentinel-dashboard-1.8.2.jar,它是一个 jar 直接用 java -jar 命令启动即可。

# 2. 运行命令

前提

java8 环境 OK

8080 端口不能被占用

命令

java -jar sentinel-dashboard-1.8.2.jar

image-20221111130111566

# 3. 访问 sentinel 管理界面

http://localhost:8080

登录账号密码均为 sentinel,刚进来是啥都没有:

image-20221111130213634

# 3 - 初始化演示工程

# 3.1. 启动 Nacos8848 成功

http://localhost:8848/nacos/#/login

# 3.2. 案例

# 1. 创建 Module:cloudalibaba-sentinel-service8401

# 2. POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2021</artifactId>
        <groupId>com.dabing.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>cloudalibaba-sentinel-service8401</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.dabing</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-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>
</project>

# 3. YML

server:
  port: 8401
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719  #默认 8719,应用与 Sentinel 控制台交互的端口,应用本地会起一个该端口占用 HttpServer
management:
  endpoints:
    web:
      exposure:
        include: '*'

img

https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-endpoints

img

# 4. 主启动

package com.dabing.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401{
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class, args);
    }
}

# 5. 业务类 FlowLimitController

package com.dabing.springcloud.alibaba.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FlowLimitController{
    @GetMapping("/testA")
    public String testA() {
        return "------testA";
    }
    @GetMapping("/testB")
    public String testB() {
        return "------testB";
    }
}

# 3.3. 启动 Sentinel8080

java -jar sentinel-dashboard-1.8.2.jar

# 3.4. 启动微服务 8401

# 3.5. 启动 8401 微服务后查看 sentienl 控制台

空空如也,啥都没有

Sentinel 采用的懒加载说明

执行一次访问即可

http://localhost:8401/testA

http://localhost:8401/testB

效果

img

结论

sentinel8080 正在监控微服务 8401

接下来可以测试 sentinel 的流控、熔断等功能。

image-20221111131820987

# 4 - 流控规则

# 4.1. 基本介绍

img

进一步解释说明

image-20221020222100417

image-20221020221945421

# 4.2. 流控模式

# 1. 直接(默认)

直接 -> 快速失败

  • 系统默认

测试 QPS

  • 配置及说明

    表示 1 秒钟内查询 1 次就是 OK, 若超过次数 1,就直接 - 快速失败,报默认错误

img

  • 快速点击访问: http://localhost:8401/testA

  • 结果

    • Blocked by Sentinel (flow limiting)

测试线程数

img

  • 快速点击访问: http://localhost:8401/testA

  • 结果

    • 不会出现 Blocked by Sentinel (flow limiting)(线程处理请求很快)

img

但是,在映射方法里添加 sleep 后,同样也会出现 Blocked by Sentinel (flow limiting) 默认提示信息。

思考???

  • 直接调用默认报错信息,技术方面 OK but,是否应该有我们自己的后续处理?

  • 类似有一个 fallback 的兜底方法?

# 2. 关联

是什么?

当关联的资源达到阈值时,就限流自己

当与 A 关联的资源 B 达到阈值后,就限流自己

B 惹事,A 挂了

# 3. 配置 A

设置效果:

当关联资源 /testB 的 QPS 阀值超过 1 时,就限流 /testA 的 REST 访问地址,当关联资源到阀值后闲置配置的的资源名。

img

# 4. postman 模拟并发密集访问 testB

访问 testB 成功

img

postman 里新建多线程集合组,将请求保存到集合组

img

运行线程集合组

img

设置并发访问参数

img

Run

  • 大批量线程高并发访问 B,导致 A 失效了

运行后发现 testA 挂了

  • 点击访问 http://localhost:8401/testA

  • 结果

    • Blocked by Sentinel (flow limiting)

# 5. 链路

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

家庭作业试试

# 4.3. 流控效果

# 1. 直接 -> 快速失败(默认的流控处理)

  • 直接失败,抛出异常:Blocked by Sentinel (flow limiting)

  • 源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

# 2. 预热

img

img

  • 限流 冷启动

https://github.com/alibaba/Sentinel/wiki/ 限流 --- 冷启动

  • 源码

com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

img

  • Warmup 配置

默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。

案例:阈值为 10 + 预热时长设置 5 秒。

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

img

§ 多次点击 http://localhost:8401/testB

§ 刚开始不行,后续慢慢 OK

§ 应用场景

・如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值。

# 3. 排队等待

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

设置含义:/testB 每秒 1 次请求,超过的话就排队等待,等待的超时时间为 20000 毫秒。

img

・官网

img

・源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController

・测试

§ 增加打印语句

@GetMapping("/testB")
public String testB() {
log.info(Thread.currentThread().getName()+"\t ...testB");
return "------testB";
}

§ 增加线程组:直接 10 个线程并发,排队被依次处理

img

# 5 - 降级规则

# 5.1. 官网

https://github.com/alibaba/Sentinel/wiki/ 熔断降级

img

# 5.2. 基本介绍

整体介绍

img

img

img

# 5.3. 降级策略实战

# 1. 慢调用比例

是什么

image-20221020222932376

测试:

代码

@GetMapping("/testA")
    public String testA() {
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "------testA";
    }

配置:

img

§ 访问测试: http://localhost:8401/testA

§ 5 秒内打进 10 个请求,由于每次请求都大于 RT,并且比例阈值 100%,所以,熔断器打开。

img

# 2. 异常比例

是什么

image-20221020223025735

测试:

代码

@GetMapping("/testB")
public String testB() {
        int age = 10/0;
        return "------testB";
}

§ 配置

img

§ 访问测试: http://localhost:8401/testB

§ 5 秒内打进 10 个请求,由于每次请求都抛异常,异常比例阈值 100% 超过 50%,所以,熔断器打开,10s 后半开。如果再次访问有异常,则继续熔断。

img

# 3. 异常数

是什么

img

测试:

代码

@GetMapping("/testB")
public String testB(){
    int age = 10/0;
    return "------testB 测试异常数";
}

配置

img

§ 访问测试: http://localhost:8401/testB

§ 5 秒内打进 10 个请求,由于每次请求都抛异常,异常数超过 5 个,所以,熔断器打开,10s 后半开。如果再次访问有异常,则继续熔断。

img

# 6 - 热点 key 限流

# 6.1. 基本介绍

是什么

img

# 6.2. 官网

https://github.com/alibaba/Sentinel/wiki/ 热点参数限流

img

# 6.3. 承上启下复习

兜底方法

分为系统默认客户自定义,两种

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

我们能不能自定义?类似 hystrix, 某个方法出现问题了,就找对应的兜底降级方法?

结论

从 @HystrixCommand 到 @SentinelResource

# 6.4. 代码

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2) {
    //int age = 10/0;
    return "------testHotKey";
}
// 兜底方法
public String deal_testHotKey (String p1, String p2, BlockException exception){
    return "------deal_testHotKey,o(╥﹏╥)o";  
}

​ com.alibaba.csp.sentinel.slots.block.BlockException

# 6.5. 配置

  1. 配置

img

img

  1. 默认
  • @SentinelResource(value = “testHotKey”)

  • 异常打到了前台用户界面,不友好

img

  1. 自定义
  • @SentinelResource (value = “testHotKey”,blockHandler = “deal_testHotKey”) //value 值与资源名一致即可

  • 方法 testHostKey 里面第一个参数只要 QPS 超过每秒 1 次,马上降级处理

  1. 测试
  • error (1 秒 1 下可以,但是,超过则降级,和 p1 参数有关)

http://localhost:8401/testHotKey?p1=abc

  • error(1 秒 1 下可以,但是,超过则降级,和 p1 参数有关)

http://localhost:8401/testHotKey?p1=abc&p2=33

  • right(狂点不会触发降级,与 p2 参数无关)

http://localhost:8401/testHotKey?p2=abc

# 6.6. 参数例外项

上述案例演示了第一个参数 p1, 当 QPS 超过 1 秒 1 次点击后马上被限流

特殊情况

普通

超过 1 秒钟一个后,达到阈值 1 后马上被限流

我们期望 p1 参数当它是某个特殊值时,它的限流值和平时不一样

特例

假如当 p1 的值等于 5 时,它的阈值可以达到 200

配置

添加按钮不能忘

img

测试

http://localhost:8401/testHotKey?p1=5

http://localhost:8401/testHotKey?p1=3

  • 当 p1 等于 5 的时候,阈值变为 200

  • 当 p1 不等于 5 的时候,阈值就是平常的 1

前提条件

  • 热点参数的注意点,参数必须是基本类型或者 String

img

# 6.7. 其他

手贱添加异常看看…

@SentinelResource

处理的是 Sentinel 控制台配置的违规情况,有 blockHandler 方法配置的兜底处理

RuntimeException

Int age = 10/0; 这个是 java 运行时报出的运行时异常 RuntimeException,@SentinelResource 不管

总结:

@SentinelResource 主管配置出错,运行出错该走异常走异常,不像 Hystrix 会走兜底的方法,Sentinel 配置的熔断规则和未配置的异常是分开的。

# 7 - 系统规则

# 7.1. 是什么

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

# 7.2. 各项配置参数说明

img

# 7.3. 配置全局 QPS

img

# 8 - @SentinelResource

# 8.1. 按资源名称限流 + 后续处理

通过访问的 URL 来限流,会返回自定义的限流处理信息

启动 Nacos 成功

启动 Sentinel 成功

Module

  • cloudalibaba-sentinel-service8401

  • POM

  • YML

server:
  port: 8401
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719  #默认 8719,应用与 Sentinel 控制台交互的端口,应用本地会起一个该端口占用的 HttpServer
management:
  endpoints:
    web:
      exposure:
        include: '*'
  • 业务类 RateLimitController
package com.dabing.springcloud.alibaba.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.dabing.springcloud.alibaba.entities.CommonResult;
import com.dabing.springcloud.alibaba.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@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 服务不可用");
    }
}
  • 主启动类
package com.dabing.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401{
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class, args);
    }
}

配置流控规则

配置步骤

img

图形配置和代码关系:

表示 1 秒钟内查询次数大于 1,就跑到我们自定义的处理,限流

测试

1 秒钟点击 1 下,OK

超过上述问题,疯狂点击,返回了自己定义的限流处理信息,限流发生

image-20221112103027304

额外问题

此时关闭微服务 8401 看看

Sentinel 控制台,流控规则消失了?????

临时 / 持久?----- 临时,所以后面需要配置进 nacos 里持久化

# 8.2. 按照 Url 地址限流 + 后续处理

通过访问的 URL 来限流,会返回 Sentinel 自带默认的限流处理信息

业务类 RateLimitController

@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl(){
    return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
}

o 访问一次

o Sentinel 控制台配置

img

测试

疯狂点击 http://localhost:8401/rateLimit/byUrl

结果

会返回 Sentinel 自带的限流处理结果

img

# 8.3. 上面兜底方法面临的问题

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

依照现有条件,我们自定义的处理方法又和业务代码耦合在一起,不直观。

每个业务方法都增加一个兜底的,那代码膨胀加剧。

全局统一的处理方法没有体现。

# 8.4. 客户自定义限流处理逻辑

创建 customerBlockHandler 类用于自定义限流处理逻辑

自定义限流处理类

方法必须是 public static 修饰的。

package com.dabing.springcloud.alibaba.myhandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.dabing.springcloud.entities.CommonResult;
public class CustomerBlockHandler {
    public static CommonResult handleException(BlockException exception){
        return new CommonResult(2020,"自定义限流处理信息.... CustomerBlockHandler --- 1");
    }
    public static CommonResult handleException2(BlockException exception){
        return new CommonResult(2020,"自定义限流处理信息.... CustomerBlockHandler --- 2");
    }
}

RateLimitController

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
        blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException2")
public CommonResult customerBlockHandler(){
    return new CommonResult(200,"按客戶自定义",new Payment(2020L,"serial003"));
}

o 启动微服务后先调用一次

http://localhost:8401/rateLimit/customerBlockHandler

o Sentinel 控制台配置

o 测试后我们自定义的出来了

o 进一步说明

img

# 8.5. 更多注解属性说明

https://github.com/alibaba/Sentinel/wiki/ 注解支持

img

/**
    * @SentinelResource  与 Hystrix 组件中的 @HystrixCommand 注解作用类似的。
    *    value = "byResourceName"  用于设置资源名称,只有根据资源名称设置的规则,才能执行 blockHandler 所引用降级方法。
    *    如果按照映射路径进行规则配置,返回默认降级消息:Blocked by Sentinel (flow limiting)
    *    blockHandler 用于引用降级方法。
    *    blockHandlerClass 用于引用降级方法的处理器类。注意:降级方法必须是 static 的。否则,无法解析
    *    blockHandler + blockHandlerClass 只处理配置违规,进行降级处理。代码出现异常,不执行的。
    *
    *    blockHandler + fallback 同时存在,配置违规,代码也有异常,这时,走 blockHandler 配置文件降级处理
    *
    *    exceptionsToIgnore 设置特定异常不需要降级处理。
    */
   @RequestMapping("/fallback/{id}")
   @SentinelResource(value = "byFallbackName",blockHandler = "handleException3",
           blockHandlerClass = RateLimitControllerHandler.class,
           fallback = "handleException2",fallbackClass = RateLimitControllerHandler.class,
           exceptionsToIgnore=IllegalArgumentException.class
   )
   public CommonResult<Payment> fallback(@PathVariable("id") Long id) {
       if (id == 4) {
           throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
       }
       if (id==-1) {
           CommonResult<Payment> result = new CommonResult<>(444,"数据不存在",null);
           throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
       }
       CommonResult<Payment> result = new CommonResult<>(200,"数据已经获取",new Payment(id,"test"+1));
       return result;
   }

# 9 - 熔断框架比较

img

# 10 - 规则持久化

# 10.1. 是什么

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

# 10.2. 怎么玩

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

# 10.3. 步骤

# 1. 修改:cloudalibaba-sentinel-service8401

# 2. POM

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

# 3. YML

server:
  port: 8401
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos 服务注册中心地址
    sentinel:
      transport:
        dashboard: localhost:8080 #配置 Sentinel dashboard 地址
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow
management:
  endpoints:
    web:
      exposure:
        include: '*'

# 4. 添加 Nacos 业务规则配置

img

・内容解析

[
    {
         "resource": "/testA",
         "limitApp": "default",
         "grade": 1,
         "count": 1,
         "strategy": 0,
         "controlBehavior": 0,
         "clusterMode": false 
    }
]

img

# 5. 启动 8401 后刷新 sentinel 发现业务规则有了

img

# 6. 快速访问测试接口

http://localhost:8401/testA

默认

img

# 7. 停止 8401 再看 sentinel

img

# 8. 重新启动 8401 再看 sentinel

扎一看还是没有,稍等一会儿

多次调用

http://localhost:8401/testA

重新配置出现了,持久化验证通过

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Dabing-He 微信支付

微信支付