sentinel

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

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

Sentinel 是什么?

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

Sentinel 具有以下特征:

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

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

安装Sentiel控制台

官方下载地址:https://github.com/alibaba/Sentinel/releases

我的腾讯网盘:链接:https://share.weiyun.com/50Nrrka 密码:eewn5u

下载到本地sentinel-dashboard-1.7.2.jar

运行

前提:java 环境配置成功,8080端口未被占用

运行命令:java -jar sentinel-dashboard-1.7.2.jar

测试访问

http://localhost:8080

默认账号密码sentinel

初始化演示功能

新建cloudalibaba-sentinel-server8401

pom

yml

controller

启动 访问 sentinel 控制台发现啥都没有,这不代表错了,因为Sentinel采用了懒加载的方式,所以我们先要访问 http://localhost:8401/testA、http://localhost:8401/testB 才能出现

流量规则

  • 资源名:唯一名称,默认请求路径
  • 针对来源: Sentinel可以针对调用者进行限流, 填写微服务名,默认default (不区分来源)
    • default :表示不区分调用者,来自任何调用者的请求都将进行限流统计。如果这个资源名的调用总和超过了这条规则定义的阈值,则出发限流。
    • : 表示针对特定的调用者,只有来自这个调用者的请求才会进行流量控制。例如NodeA 配置了一条针对调用者caller1 的规则,那么当且仅当来自caller1 对 NodeA 的请求才会触发流量控制。
    • other :表示针对除 以外的其余调用方的流量进行流量控制。例如:资源NodeA 配置了一条针对调用者caller1 的限流规则,同时又配置了一条调用者为other 的规则,那么任意来自非caller1 对NodeA 的调用,都不能超过other这条规则定义的阈值。
  • 阅值类型/单机阈值:
    • QPS (每秒钟的请求数量) :当调用该ap的QPS达到问值的时候,进行限流
    • 线程数:当调用该api的线程数达到问值的时候,进行限流
  • 是否集群:不需要集群
  • 流控模式:
    • 直接: ap达到限流条件时,直接限流
    • 关联:当关联的资源达到问值时,就限流自己
    • 链路:只记录指定链路,上的流量(指定资源从入口资源进来的流量,如果达到阅值,就进行限流) [api级别的针对来源]
  • 流控效果:
    • 快速失败:直接失败,抛异常
    • Warm Up: 根据codeFactor (冷加载因子,默认3)的值,从阈值codeFactor, 经过预热时长,才达到设
      置的QPS阈值
    • 排队等待:匀速排队,让请求以匀速的速度通过,闽值类型必须设置为QPS,否则无效

流控模式

直接:QPS快速失败

QPS一秒内访问次数 1

新增留空规则后访问http://localhost:8401/testA

一秒点一次 是可以正常访问的,但是快速访问多次他会报错 Blocked by Sentinel(flow limiting)

关联

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

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

简单说 testA 将testB 关联 之后, testB出现QPS 超过了。testA也会

当关联资源/testB的qps阀值超过1时,就限流/testA的Rest访问地址, 当关联资源到阈值后限制配置好的资源名

测试我们可以通过postman 工具或者其他的来帮助我们测试

链路

https://blog.csdn.net/ooyhao/article/details/102745914#%E6%A0%B9%E6%8D%AE%E8%B0%83%E7%94%A8%E9%93%BE%E8%B7%AF%E9%99%90%E6%B5%81

流控效果

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

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

预热

说明:公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值
默认coldFactor为3,即请求QPS从threshold/3开始,经预热时长逐渐升至设定的QPS阈值

Warm Up

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

通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:

配置

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

一般应用场景

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

排队等待

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

该方式的作用如下图所示:

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

配置

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

熔断降级

概述

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

降级策略

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

降级策略实例

RT

添加方法

@GetMapping("/testRT")
public String testRT(){
    log.info(Thread.currentThread().getName() + "...testRT ");
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "testRT   -----";
}

通过压力测试工具永远一秒钟打进来10个线程(大于5个了)调用testD, 我们希望200毫秒处理完本次任务,
如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了,过了这一秒的服务又可以访问了

异常比例

在controller添加

@GetMapping("/testException")
public String testException(){
    log.info("testException 异常比例");
    int age = 10 /0 ;
    return "testException -----";
}

设置异常比例降级

直接高并发发送请求,多次调用达到我们的配置条件了。断路器开启(保险丝跳闸),微服务不可用了,不再报错error而是服务降级了

异常数

异常数是按分钟统计的

添加方法

@GetMapping("/testExceptionCount")
public String testExceptionCount(){
    log.info("testExceptionCount 异常数");
    int age = 10 /0 ;
    return "testExceptionCount -----";
}

设置异常数配置

测试访问http://localhost:8401/testExceptionCount 第六次访问会发现sentinel启动了降级,之后要等61 s 后再能正常访问

sentinel 热点参数限流

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

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

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

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

简单示例

首先你得引入了

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

添加方法测试

/**
     * @SentinelResource 中
     *      value 是用于在 sentinel控制台上 热点规则中的 方便指定唯一的 资源名
     *      blockHandler 是当这个包含参数访问次数达到了我们在sentinel中设置的QPS的值时的兜底方法,要是不设置
     *      当 达到了QPS 异常信息就打到了前台用户界面看到,不友好,所以一般要用热点key限流都会配置这个
     * @param p1
     * @param p2
     * @return
     */
    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey", blockHandler = "dealTestHotKey")
    public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
                             @RequestParam(value = "p2", required = false) String p2){
//        int age = 10 /0;
        return "testHotKey -----";
    }

    /**
     * 兜底方法
      * @param p1
     * @param p2
     * @param blockException
     * @return
     */
    public String dealTestHotKey(String p1, String p2, BlockException blockException){
        return "dealTestHotKey---------";
    }

配置sentinel热点key

测试 https://localhost:8401/testHotKey?p1=abc 一开始一秒访问一次,当快速点击时他就开始启动降级了,打出了我们自定义的错误,因为我们sentinel中配置了参数索引为0 所以 只要这个链接中带有p1 就会 生效 热点规则

参数例外项

简单的说就是设置特殊的不受上面配置的热点参数规则的限制

他只支持这几种参数类型

还有一点要注意的是:热点参数限流他不会给 运行时异常提供兜底方法,所以不要搞混了,但是后面会有方法

系统自适应限流

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 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 达到阈值即触发系统保护。

不合适,使用危险,一竹竿打死一船人

@SentinelResource

源码

/**
 * The annotation indicates a definition of Sentinel resource.
 *
 * @author Eric Zhao
 * @since 0.1.1
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {

    /**
     * @return name of the Sentinel resource
     */
    String value() default "";

    /**
     * @return the entry type (inbound or outbound), outbound by default
     */
    EntryType entryType() default EntryType.OUT;

    /**
     * @return the classification (type) of the resource
     * @since 1.7.0
     */
    int resourceType() default 0;

    /**
     * @return name of the block exception function, empty by default
     */
    String blockHandler() default "";

    /**
     * The {@code blockHandler} is located in the same class with the original method by default.
     * However, if some methods share the same signature and intend to set the same block handler,
     * then users can set the class where the block handler exists. Note that the block handler method
     * must be static.
     *
     * @return the class where the block handler exists, should not provide more than one classes
     */
    Class<?>[] blockHandlerClass() default {};

    /**
     * @return name of the fallback function, empty by default
     */
    String fallback() default "";

    /**
     * The {@code defaultFallback} is used as the default universal fallback method.
     * It should not accept any parameters, and the return type should be compatible
     * with the original method.
     *
     * @return name of the default fallback method, empty by default
     * @since 1.6.0
     */
    String defaultFallback() default "";

    /**
     * The {@code fallback} is located in the same class with the original method by default.
     * However, if some methods share the same signature and intend to set the same fallback,
     * then users can set the class where the fallback function exists. Note that the shared fallback method
     * must be static.
     *
     * @return the class where the fallback method is located (only single class)
     * @since 1.6.0
     */
    Class<?>[] fallbackClass() default {};

    /**
     * @return the list of exception classes to trace, {@link Throwable} by default
     * @since 1.5.1
     */
    Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
    
    /**
     * Indicates the exceptions to be ignored. Note that {@code exceptionsToTrace} should
     * not appear with {@code exceptionsToIgnore} at the same time, or {@code exceptionsToIgnore}
     * will be of higher precedence.
     *
     * @return the list of exception classes to ignore, empty by default
     * @since 1.6.0
     */
    Class<? extends Throwable>[] exceptionsToIgnore() default {};
}

属性说明

参考源码的注释,逐个解释下这几个属性的作用。

value

资源名称,必需项,因为需要通过resource name找到对应的规则,这个是必须配置的。

entryType

entry 类型,可选项,
有IN和OUT两个选项,默认为 EntryType.OUT。

public enum EntryType {
    IN("IN"),
    OUT("OUT");
}

blockHandler

blockHandler 对应处理 BlockException 的函数名称,可选项。
blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,
参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。

blockHandlerClass

blockHandler 函数默认需要和原方法在同一个类中,如果希望使用其他类的函数,
则需要指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

fallback

fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。
fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。

fallbackClass

fallbackClass的应用和blockHandlerClass类似,fallback 函数默认需要和原方法在同一个类中。
若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

defaultFallback(since 1.6.0)

如果没有配置defaultFallback方法,默认都会走到这里来。
默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑。
默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。
若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。

exceptionsToIgnore(since 1.6.0)

用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

解决高耦合的问题

通过解析了这个注解我们可以解决之前代码的一个小问题,就是兜底方法和业务代码写在一块,高耦合

之前的写法

我们通过blockHandlerClass这个属性来分开后的效果

blockHandler指定的方法可以切换CustomerBlockHandler中的其他方法

OpenFeign的集成配置

引入依赖

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

在yam中添加配置

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

之后就能使用openfeign调用

sentinel持久化

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

为什么要配置

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

简单示例

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

需要引入该依赖

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

添加yaml配置

# 流控规则持久化到nacos
datasource:
  dsl:
    nacos:
      server-addr: localhost:8848
      data-id: ${spring.application.name}
      group-id: DEFAULT_GROUP
      data-type: json
      rule-type: flow

在 nacos 控制台中 添加配置文件

内容解析:

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

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

配置完之后,刷新sentinel之后会发现,留空规则中多了一条

/testA的留空规则

一开始我以为,可以直接通过sentinel配置,然后nacos 直接就创建了,后来发现只能通过nacos 来配置持久化

更多配置

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

只有不断的努力才会有更大的惊喜等着你去发现!!