SpringCloud_Alibaba(熔断与限流)

1、Sentinel

image.png

2、初始化演示工程

  • 启动nacos
  • 启动Sentinel 8080
  • 新建 cloudalibaba-sentinel-service8401
  • maven依赖
    <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>
  • yml
server:
  port: 8401

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

management:
  endpoints:
    web:
      exposure:
        include: '*'
  • 启动类
@SpringBootApplication
@EnableDiscoveryClient
public class MainApp8401 {
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class,args);
    }
}
  • controller
@RestController
public class FlowLimitController {
    @GetMapping("/testA")
    public String testA()
    {
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB()
    {
        return "------testB";
    }
}
  • 启动 cloudalibaba-sentinel-service8401

  • Sentinel采用了懒加载,要先访问controller接口,再访问Sentinel控制台

image.png

3、流控规则

3.1、基本介绍

image.png

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

3.2、流控模式

  • 直接(默认)
  • 如果一秒钟请求超过一次就直接快速失败报默认错误

image.png

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

image.png

  • 链路

3.3、流控效果

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

  • 预热
  • Warm Up方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过“冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值的上限,给冷系统一个预热时间,避免冷系统被压垮。
  • 公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值
  • 默认 coldFactor 为 3,即请求QPS从(threshold / 3) 开始,经多少预热时长才逐渐升至设定的 QPS 阈值。
  • 系统初始化的阀值为10 / 3 约等于3,即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10

image.png

  • 排队等待
  • 匀速排队,阈值必须设置为QPS
  • 匀速排队方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法

image.png

4、降级规则

image.png

  • RT(平均响应时间,秒级)
    • 平均响应时间 超过阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级
    • 窗口期过期关闭断路器
    • RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)
  • 异常比例(秒级)
    • QPS >= 5 且异常比例超过阈值,触发降级,时间窗口结束后,关闭降级。异常比例的阈值范围[0.0,1.0],代表 0% - 100%
  • 异常数(分钟级)
    • 当资源近一分钟的异常数超过阈值时,触发降级,时间窗口结束后,关闭降级。由于统计时间是分钟级的,若时间窗口小于60s,则结束熔断状态后仍可能再进入熔断状态,时间窗口一定要大于60s

  • Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制
  • 让请求快速失败,避免影响到其他的资源而导致级联错误
  • 当资源被降级后,在接下来的时间窗口之内,对该资源的调用都会自动熔断(默认行为是抛出DegradeException)
  • Sentinel的断路器是没有半开状态的

5、热点key限流

  • 热点级经常访问的数据,希望统计或者限制某个热点数据,并对其访问进行限流或者其他操作

image.png

  • controller
@RestController
public class FlowLimitController {

    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
    public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                             @RequestParam(value = "p2",required = false) String p2){
        return "------testHotKey";
    }
    
    public String dealHandler_testHotKey(String p1, String p2, BlockException exception)
    {
        return "-----dealHandler_testHotKey";
    }
}
  • 新增热点规则

image.png

  • 第一个参数有值的话,1秒的QPS为1,超过就限流,限流后调用dealHandler_testHotKey支持方法。
    image.png

  • 如果参数中没有 p1 这个参数则不会进行限流

image.png


  • 上述案例中第一个参数p1达到阈值1后马上被限流
  • 期望p1参数当它是某个特殊的值时,它的限流值和平时不一样
  • 如果p1的值等于5时,它的阈值可以达到10

image.png

6、系统规则

  • 系统保护规则是从应用级别入口流量进行控制,从单台及其的load、cpu使用率、平均RT、入口QPS和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性
  • 系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效
  • 系统规则支持以下模式:
    • 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达到阈值即触发系统保护

image.png

7、@SentinelResource

  • 按资源名称限流
  • controller
@RestController
public class RateLimitController {

    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public String byResource(){
        return "按照资源名称限流";
    }

    public String handleException(BlockException exception)
    {
        return "服务不可用";
    }
}
  • 配置Sentinel添加限流

image.png

  • 此时关闭服务8401,Sentinel控制台,流控规则消失

  • 按url地址限流
  • 通过访问的url来限流,会返回Sentinel自带的默认的限流信息
  • controller
@RestController
public class RateLimitController {

    @GetMapping("/rateLimit/byUrl")
    @SentinelResource(value = "byUrl")
    public String byUrl()
    {
        return "按照url限流";
    }
}
  • 配置Sentinel添加限流

image.png


  • 上述兜底方案面临的问题

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

  • 自定义限流处理类

public class CustomerBlockHandler {
    public static String handleException1(BlockException exception){
        return "自定义限流处理信息1";
    }

    public static String handleException2(BlockException exception){
        return "自定义限流处理信息2";
    }
}
  • controller
@RestController
public class RateLimitController {
    /**
     * blockHandlerClass = CustomerBlockHandler.class
     * blockHandler = "handleException1"
     * 上述配置:找CustomerBlockHandler类里的handleException1方法进行兜底降级处理
     */
    @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException1")
    public String customerBlockHandler()
    {
        return "按客户自定义限流处理逻辑";
    }
}
  • 配置Sentinel添加限流

image.png

8、服务熔断功能

8.1、Ribbon系列

  • sentinel整合ribbon + openFeign + fallback
  • 启动nacos和sentinel
  • 新建cloudalibaba-provider-payment9003/9004
  • maven依赖
    <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </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>
  • yml
server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'
  • controller
@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;


    @GetMapping(value = "/payment/{id}")
    public String paymentSQL(@PathVariable("id") Long id)
    {
        return "id:"+id+" from server port:"+serverPort;
    }

}
  • 启动类
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9003.class,args);
    }
}
  • 新建cloudalibaba-consumer-order86
  • maven依赖
    <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>
        <!-- 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>
  • yml
server:
  port: 86
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
  • config
@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}
  • controller
@RestController
public class OderController {
    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Autowired
    private RestTemplate restTemplate;

    /**
     * fallback 运行异常
     * blockHandler 配置违规
     * exceptionsToIgnore 忽略,如果报指定异常,fallback方法无法使用,没有降级效果
     */
    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",
            fallback = "handlerFallback",
            blockHandler = "blockHandler",
            exceptionsToIgnore = {IllegalArgumentException.class})
    public String fallback(@PathVariable Long id)
    {
        if (id == 1002) {
            throw new IllegalArgumentException ("非法参数异常....");
        }

        return restTemplate.getForObject(SERVICE_URL + "/payment/" + id, String.class);
    }
    public String handlerFallback(@PathVariable  Long id,Throwable e) {
        return "handlerFallback---->"+e.getMessage();
    }
    public String blockHandler(@PathVariable  Long id, BlockException blockException) {
        return "blockHandler";
    }
}

8.2、Feign系列

  • consumer添加openfeign依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
  • yml激活对openfeign的支持
# 激活Sentinel对Feign的支持
feign:
  sentinel:
    enabled: true 
  • service接口
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {
    @GetMapping(value = "/payment/{id}")
    public String payment(@PathVariable("id") Long id);
}
  • 实现service接口
@Component
public class PaymentFallbackService implements PaymentService{
    @Override
    public String payment(Long id) {
        return "服务降级 ---> " + id;
    }
}
  • controller
@RestController
public class OrderController {

    @Autowired
    private PaymentService paymentService;

    @GetMapping(value = "/consumer/openfeign/{id}")
    public String payment(@PathVariable("id") Long id)
    {
        return paymentService.payment(id);
    }
}
  • 启动类开启 openfeign功能
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class OrderNacosMain84
{
    public static void main(String[] args) {
            SpringApplication.run(OrderNacosMain84.class, args);
    }
}

8.3、熔断框架比较

Sentinel Hystrix
隔离策略 信号量隔离(并发线程数限流) 线程池隔离/信号隔离
熔断降级策略 基于响应时间、异常比率、异常数 基于异常比例
实时统计实现 滑动窗口(LeapArray) 滑动窗口(基于RxJava)
动态规则配置 支持多种数据源 支持多种数据源
扩展性 多个扩展点 插件的形式
基于注解支持 支持 支持
限流 基于QPS、支持基于调用关系的限流 有限的支持
流量整型 支持预热模式、匀速器模式、预热排队模式 不支持
系统自适应保护 支持 不支持
控制台 提供开箱即用的控制台,可配置规则、查看秒级监控、及其发现等 简单的监控查看
posted @ 2022-11-06 22:42  youmo~  阅读(70)  评论(0编辑  收藏  举报