Spring Cloud(二十)Sentinel 流量控制和服务降级

Table of Contents

1.Sentinel介绍

2.Sentinel 与 Hystrix 的对比

3.Sentinel 安装

4.初始化工程演示

1.新建alibaba-sentinel-service8401

2.测试

5.流量控制

1.流控模式-直接:QPS(每秒请求数) 1次

2.流控模式-直接:线程数

3.流控模式-关联

4.流控效果-预热(Warm Up)

5.流控效果-排队等待

6.熔断降级

1.降级策略-RT

2.降级策略-异常比例

3.降级策略-异常数

7.热点参数限流

1.基本配置测试

2.参数例外项测试

8.系统规则

9. @SentinelResource

1.说明

2.按资源名称限流

3.按URL限流

4.自定义限流处理逻辑

10. 服务熔断功能

1.Ribbon系列

2.Openfeigh系列

11. 规则持久化



1.Sentinel介绍

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

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

中文:https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/zh-cn/index.htm

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

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

 

2.Sentinel 与 Hystrix 的对比

 

3.Sentinel 安装

链接:https://pan.baidu.com/s/1pLeCEoQpYX0npG3lTncjCg 
提取码:wcgc

 

启动

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

http://localhost:8080/sentinel

账号:sentinel 密码:sentinel

 

4.初始化工程演示

1.新建alibaba-sentinel-service8401

pom

<dependencies>
        <!--后续持久化使用-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!--sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--opentfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ak.demo</groupId>
            <artifactId>api-commons</artifactId>
            <version>${project.version}</version>
        </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>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

yml

server:
  port: 8401
spring:
  application:
    name: sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
#        server-addr: 192.168.71.128:1111 #通过nginx注册到 nacos集群
    sentinel:
      transport:
        dashboard: localhost:8080 #sentinel dashboard地址
        port: 8719 #默认为8719端口,如果被占用会自动从8719开始一次+1,直到找到未被占用的端口

management:
  endpoints:
    web:
      exposure:
        include: "*"

业务

@EnableDiscoveryClient
@SpringBootApplication
public class SentinelService8401 {
    public static void main(String[] args) {
        SpringApplication.run(SentinelService8401.class,args);
    }
}
@RestController
public class FlowLimitController {
    @GetMapping("testA")
    public String testA() {
        return "===testA";
    }

    @GetMapping("testB")
    public String testB() {
        return "===testB";
    }
}

2.测试

分别启动nacos8848服务中心、sentinel8080、8401

打开sentinel8080,没有展示8401,由于sentinel采用懒加载,8401需要被访问才能被初始化

http://localhost:8401/testA

http://localhost:8401/testB

 

 

5.流量控制

原理:监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

1.流控模式-直接:QPS(每秒请求数) 1次

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

 

2.流控模式-直接:线程数

当调用该 api 的线程数达阈值的时候,进行限流

  @GetMapping("testA")
    public String testA() {
        try {
            TimeUnit.MILLISECONDS.sleep(800);//0.8s
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "===testA";
    }

 

 

 

3.流控模式-关联

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

postman并发访问 /testB,同时浏览器访问testA报错

public class FlowLimitController {
    @GetMapping("testA")
    public String testA() {
        return "===testA";
    }

    @GetMapping("testB")
    public String testB() {
        return "===testB";
    }
}

 

4.流控效果-预热(Warm Up)

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

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

源码

配置测试

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

 

5.流控效果-排队等待

如果设置了RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER,则请求将以固定速率通过。

当新请求到达时,流规则将检查新请求和先前请求之间的间隔。如果间隔长于规则中的值,则请求可以通过;否则,请求可以通过。否则,哨兵将计算此请求的等待时间。并且,如果请求必须等待的时间超过规则中的“超时”值,则该请求将被立即拒绝。

速度限制器广泛用于脉冲流。当大量流量到达时,系统不会立即处理所有请求,而是在接下来的几秒钟内稳定地处理这些请求。

测试结果:每秒只允许一个请求通过

 

6.熔断降级

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException

Sentinel的断路器是没有半开状态的。

降级策略

我们通常用以下几种方式来衡量资源是否处于稳定的状态:

  • 平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 N 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。
  • 异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= N(可配置),并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (DEGRADE_GRADE_EXCEPTION_COUNT):当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。

1.降级策略-RT

 

2.降级策略-异常比例

 

3.降级策略-异常数

 

7.热点参数限流

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。

 

1.基本配置测试

    @GetMapping("testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "dealTestHotKey")
    public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                             @RequestParam(value = "p2",required = false) String p2) {
        return "testHotKey";
    }

    public String dealTestHotKey(String p1, String p2, BlockException exception){
        return "dealTestHotKey";//sentinel默认提示:Blocked by Sentinel (flow limiting)
    }

测试结果

http://localhost:8401/testHotKey?p1=1&p2=2  含p1时,并超过每秒一次请求,会失败

http://localhost:8401/testHotKey?p2=2  不含p1时,并超过每秒一次请求,不影响

 

注意:不加 blockHandler = "dealTestHotKey"处理,会是错误页面

 

2.参数例外项测试

参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型和字符串类型

 

 

8.系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则支持以下的模式:

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

 

 

 

9. @SentinelResource

1.说明

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

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

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

注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理

特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandlerfallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

2.按资源名称限流

@RestController
public class RateLimitController {
    @GetMapping("byResource")
    @SentinelResource(value = "byResource", blockHandler = "handleException")
    public CommonResult byResource() {
        return new CommonResult(200, "按资源名称限流 test ok", new Payment(2020L, "serial001"));
    }

    public CommonResult handleException(BlockException exception) {
        return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用");
    }
}

 

 

3.按URL限流

    @GetMapping("byUrl")
    @SentinelResource(value = "byUrl")
    public CommonResult byUrl() {
        return new CommonResult(200, "按Url限流 test ok", new Payment(2020L, "serial002"));
    }

返回系统自带错误提示

 

4.自定义限流处理逻辑

问题:

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

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

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

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

解决:

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

    public static CommonResult handleException2(BlockException exception) {
        return new CommonResult(444, "自定义的限流处理信息 CustomerBlockHandler handleException2");
    }
}
    @GetMapping("customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
    blockHandlerClass = CustomerBlockHandler.class,blockHandler = "handleException1")
    public CommonResult customerBlockHandler() {
        return new CommonResult(200, "按自定义限流 test ok", new Payment(2020L, "serial003"));
    }

 

10. 服务熔断功能

 

1.Ribbon系列

参照 alibaba-provider8901、alibaba-provider8902、alibaba-consumer8903 模块

nacos默认整合ribbon负载均衡

8903pom增加

        <!--sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

8903yml增加

    sentinel:
      transport:
        dashboard: localhost:8080 #sentinel dashboard地址
        port: 8719 #默认为8719端口,如果被占用会自动从8719开始一次+1,直到找到未被占用的端口
@RestController
public class CircleBreakerController {
    private final static String PAYMENT_URL = "http://alibaba-provider";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping(value = "consumer/fallback/{id}")
//    @SentinelResource(value = "fallback")//没有配置处理
//    @SentinelResource(value = "fallback",fallback = "handlerFallback")//配置fallback处理,fallback管运行异常
//    @SentinelResource(value = "fallback",blockHandler = "blockHandler")//配置blockHandler处理,blockHandler管配置违规,异常还是错误页面
    //配置fallback处理、blockHandler处理:
    //若blockHandler和fallback都进行了配置,
    //则被限流降级而抛出BlockException时只会进入BlockHandler处理逻辑
//    @SentinelResource(value = "fallback",
//            fallback = "handlerFallback",
//            blockHandler = "blockHandler")
    @SentinelResource(value = "fallback",
            fallback = "handlerFallback",
            blockHandler = "blockHandler",
            exceptionsToIgnore = IllegalArgumentException.class)//出现异常 IllegalArgumentException,不走 handlerFallback
    public String echo(@PathVariable Long id) {
        if (id==500){
            throw new IllegalArgumentException("非法参数");
        }
        return restTemplate.getForObject(PAYMENT_URL + "/echo", String.class);
    }
    public String handlerFallback(@PathVariable Long id,Throwable e) {
        return "handlerFallback:"+id+" massage:"+e.getMessage();
    }
    public String blockHandler(@PathVariable Long id, BlockException e) {
        return "blockHandler:"+id+" massage:"+e.getMessage();
    }
}

fallback管运行异常

blockHandler管配置违规,只针对sentinel控制台配置起作用,对业务逻辑抛出的异常不处理,还是错误页面

 

2.Openfeigh系列

8903pom增加

 <!--整合 open feign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>

主启动类加 @EnableFeignClients

 

11. 规则持久化

alibaba-sentinel-service8401

pom

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

参考:https://github.com/alibaba/Sentinel/tree/master/sentinel-extension/sentinel-datasource-nacos

 

yml

      #持久化 sentinel
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: sentinel-service
            groupId: DEFAULT_GROUP
            dataType: json
            ruleType: flow

nacos配置

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

解释:

[{
    "resource": "/testA",// 资源名称
    "limitApp": "default",// 来源应用
    "grade": 1,// 阈值类型:0表示线程数, 1表示QPS
    "count": 1,// 单机阈值
    "strategy": 0,// 流控模式:0表示直接, 1表示关联, 2表示链路
    "controlBehavior": 0,// 流控效果, 0表示快速失败, 1表示Warm Up, 2表示排队等待
    "clusterMode": false // 是否集群
}]

sentinel:访问 http://localhost:8401/testA

 

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值