SpringCloudAlibaba-Sentinel 2.2.1最新版

本文编写自2021年4月13日,当前控制台最新版本为 2021年2月4日发布的1.8.1版本

本文使用版本控制为
spring-boot-dependencies:2.4.4
spring-cloud-dependencies:2020.0.2
spring-cloud-alibaba-dependencies:2.2.1.RELEASE

本文使用版本为(受版本控制会自动选择,也不用太关注,这里只是展示一下而已)
SpringCloudAlibaba:2.2.1.RELEASE
(其中包含的Sentinel版本:1.7.1)
Sentinel控制台:v1.8.1
SpringBoot:2.4.4

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

Sentinel 官网:https://sentinelguard.io/zh-cn/
Sentinel GitHub:https://github.com/alibaba/Sentinel/
Sentinel 中文文档:https://github.com/alibaba/Sentinel/wiki/介绍

就和Hystrix差不多,实现服务降级、服务熔断。

本文默认阅读者已经熟练掌握Maven、SpringBoot等相关知识。
一些概念性东西等,源于官方文档,可以直接阅读官方文档。

概述

什么是 Sentinel

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性。

Sentinel 分为两个部分:

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

特征

Sentinel 具有以下特征:

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

Sentinel 的主要特性:

Sentinel 的开源生态

Sentinel 功能和设计理念

流量控制

流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:

流量控制有以下几个角度:

  • 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
  • 运行指标,例如 QPS、线程池、系统负载等;
  • 控制的效果,例如直接限流、冷启动、排队等。

Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。

熔断降级

什么是熔断降级

除了流量控制以外,降低调用链路中的不稳定资源也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。这个问题和 Hystrix 里面描述的问题是一样的。


Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。

熔断降级设计理念

在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。
Hystrix 通过线程池的方式,来对依赖(在我们的概念中对应资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本,还需要预先给各个资源做线程池大小的分配。
Sentinel 对这个问题采取了两种手段:

  • 通过并发线程数进行限制

和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。

  • 通过响应时间对资源进行降级

除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

系统负载保护

Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。
针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

对比

Sentinel & Hystrix:
image.png

下载安装Sentinel控制台

Sentinel这个东西使用的时候只需要导入一个pom依赖就好了,不需要像Nacos一样需要下载一个服务并运行。
但是,Sentinel它有一个控制台,这个东西是单独一个jar工程,需要我们单独下载并运行的。

下载

我们现在是2021年4月13日,最新版本控制台是2021年2月4日更新的 v1.8.1

两种方式:

  • 从最新版本的源码自行构建 Sentinel 控制台:
  • release 页面 下载最新版本的控制台 jar 包。

下载源码并编译

  • 下载 控制台 工程
  • 使用以下命令将代码打包成一个 fat jar: mvn clean package

下载编译好的jar包

进入https://github.com/alibaba/Sentinel/releases页面获取最新的Sentinel控制台jar包。
(当然你也可以根据需要寻找旧版本下载)
也可以从我的csdn下载:https://download.csdn.net/download/wangguohui0726/16660808

启动

因为是jar包项目,所以无需安装,直接启动即可。
控制台配置的规则(流控规则、熔断规则……)都是临时的。微服务重启就会消失。

注意:启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本。
使用如下命令启动控制台(最后面的文件名据实修改):

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

其中 -Dserver.port=8080 用于指定 Sentinel 控制台端口为 8080,也可修改成自己指定的端口。

从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel。可以参考 鉴权模块文档 配置用户名和密码。

注:若您的应用为 Spring Boot 或 Spring Cloud 应用,您可以通过 Spring 配置文件来指定配置,详情请参考 Spring Cloud Alibaba Sentinel 文档

启动之后的页面(默认用户名和密码都是 sentinel):
http://localhost:8080/#/login
image.png
登录之后页面(默认用户名和密码都是 sentinel):
image.png
Sentinel控制台采用的是懒加载的机制,哪怕你有别的服务使用了控制台,他这里初始也不显示,需要你访问一次那个服务的接口之后,刷新后才会展示。如果不理解,看到后面用的时候就知道了。

Sentinel的代码案例

SpringCloudAlibaba环境使用(其他使用查看官方文档):
先自己创建一个maven或是Spring Initializr项目,然后再创建我们所需要的子模块,我们要创建好多个子模块,作为服务的提供者和消费者。具体创建过程项目、子模块过程略。。。
我创建的项目名称叫做 SpringCloudAlibabaSentinel

对于依赖管理,我们可以在项目的 pom.xml 中加入如下配置控制SpringCloudAlibaba各组件的版本。
具体获取位置:在我们进入到官方文档后,左侧目录第二项 **2. Dependency Management **里面就有。
页面位置链接:https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html#_dependency_management
image.png

最终完整版父项目的 pom.xml 文件添加以下配置,统一管理子模块的依赖坐标版本(如果父pom不写也行,那么每个子模块就都要写一遍了)。

    <packaging>pom</packaging>

		<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.4.4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2020.0.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

官方demo

https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
创建子模块–>改pom–>写配置–>写代码–>启动
pom.xml

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

SpringMVC代码

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(ServiceApplication.class, args);
    }
}

@Service
public class TestService {

    @SentinelResource(value = "sayHello")
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

@RestController
public class TestController {

    @Autowired
    private TestService service;

    @GetMapping(value = "/hello/{name}")
    public String apiHello(@PathVariable String name) {
        return service.sayHello(name);
    }
}

SpringWebflux代码

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(ServiceApplication.class, args);
    }

}

@RestController
public class TestController {

    @GetMapping("/mono")
    public Mono<String> mono() {
	return Mono.just("simple string");
    }

}

application.yml

server:
  port: 9031
spring:
  cloud:
    sentinel:
      transport:
        # 这个是微服务和控制台交互的端口,默认8719,冲突时自增寻找不冲突的使用
        port: 8719
        # 这个填写我们的Sentinel控制台对应地址
        dashboard: localhost:8080
  application:
    name: AliDemo

然后启动,访问对应接口,可以在控制台看到响应监控信息
image.png

流量控制

学习此处建议先阅读官方文档 [Sentinel工作主流程](Sentinel 工作主流程),了解其相关概念。
然后阅读 官方文档-流量控制

总体介绍

我们在Sentinel的控制台上面可以看到一个叫做流控规则的菜单,在这里面我们可以看到已经创建好的流控规则,右上角可以创建新的流控规则:
image.png
image.png
也可以在簇点链路菜单里直接对某个接口进行配置:
image.png
然后我们对这个流控规则进行一个解释:

  • 资源名: 唯一名称,就是我们mvc配置的请求路径
  • 针对来源: Sentinel可以针对调用者(微服务名)进行限流,默认default(不区分来源)
  • 阈值类型/单机阈值
    • QPS(每秒请求数量):当调用该接口的QPS达到阈值的时候,进行限流
    • 线程数:当调用该接口的线程数达到阈值的时候,进行限流
  • 是否集群: (勾选和不勾选的配置项会有差别)
    • 勾选:是集群
    • 未勾选:不集群
  • 阈值:能够允许的 QPS/线程数 最大值
    • 单机阈值:非集群情况下,单机节点的阈值
    • 均摊阈值:集群环境下、单机均摊模式下,每个节点的QPS/线程数阈值
    • 总体阈值:集群环境下、总体阈值模式下、整个集群的QPS/线程数阈值
  • 流控模式
    • 直接:接口达到限流条件时,直接限流
    • 关联:当关联的资源达到限流阈值时,就限流自己
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到峰值,就进行限流)
  • 流控效果
    • 快速失败:直接抛异常
    • Warm Up:根据coldFactor(冷加载因子,默认3)的值,从阈值/coldFactor,经过预热时长,才达到设置的QPS阈值
    • 排队等待:匀速排队,让请求以匀速通过,阈值类型必须设置为QPS,否则无效
  • 失败退化: 如果 Token Server 不可用是否退化到单机限流
    • 勾选:退化
    • 未勾选:不退化

流控模式

流控模式分为三种,直接、关联、链路(上面也说了)。

  • 直接:接口达到限流条件时,直接限流
  • 关联:当关联的资源达到限流阈值时,就限流自己
  • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到峰值,就进行限流)

直接

这里只是简单使用并展示Sentinel的流控功能,更深入的使用,我们后面再说,先不要考虑我们怎么配置失败后的指定页面或者啥啥啥的。
以我们已有的服务为例子,对我们的 /hello/{name} 接口进行限流。
左侧菜单->簇点链路->/hello/{name}接口->流控->填写配置信息->新增

阈值类型:QPS,阈值:1:
代表我访问此接口的时候,一秒内连续访问超过1次会被限流。
image.png
然后它会自动跳转到"链路规则"菜单,右侧展示我们配置的流控规则。
image.png
然后进行验证:
未添加流控规则的时候,我们访问http://localhost:9031/hello/aaa 无论怎么访问,访问多少次都是没问题的。
image.png
当我们配置了阈值为1的流控规则的时候,当我们连续访问此接口超过1时,会展示如下内容。
image.png
说明我们的限流配置成功了。

之前我们配置的是QPS的,然后我们再尝试一下线程数的
阈值类型:线程数,阈值:1:
代表我访问此接口的时候,一秒内超过一个线程访问会被限流。
将流控规则中的阈值类型修改为线程数并保存。然后我们像之前一样疯狂访问,发现并没有什么效果,无论怎么访问都ok。
这是因为,我们当前配置的是线程数,不是QPS了,我们在一个标签页访问,无论怎么访问都是一个线程,一直没有超出阈值,所以就没有看到被限流的页面了。
我们可以再增加一个标签页,两个标签页都是访问这个接口,然后两个页面来回切换,并快速刷新(刷新就相当于访问了),只要手速够快,我们就会发现有一个标签页看到了被限流的页面。(如果手速不够,可以把配置修改的长一点,比如阈值改为2秒、3秒……)
image.png

关联

这个模式是在一个接口达到设定的阈值之后,对另外一个接口进行流控的操作。
为了能够实验,我们本来只有一个接口,我们将 /hello/{name} 接口复制一份,叫做 /hello2/{name} 并重启服务。(别忘记了Sentinel控制台是懒加载的,要想页面展示接口,我们需要都访问一遍)
配置:

这里我们就只展示一个QPS类型的吧,线程数的情况的话,直接模式的看懂了这里线程数的也就会了。

image.png
然后我们启动jmeter或者是postman或其他的工具对 /hello2/{name} 接口进行0.3秒一次的循环访问,启动后我们再对/hello/{name}接口进行访问,就会发现 /hello/{name} 接口被限流了。
我这里用的是jmeter,工具怎么使用我这里就不说了。不过手速快的话,两个标签页,一个hello2,一个hello,疯狂访问hello2,然后访问一次hello也能展示出效果。
image.pngimage.pngimage.png

链路

链路限流是相当于配置某一条链路访问到这个api才会导致限流,下面展示我们的接口结构图以及控制台配置图
image.pngimage.png
就比如我配置的api是sayHello这个方法(需要加上@SentinelResource注解),入口是 /hello/123 ,这时候,我们通过/hello/123 访问 sayHello 这个方法达到阈值是会进行限流的,但是仅仅只是对 /hello/123 调用 sayHello 这条链路进行限流,/hello2/123 调用 sayHello 是不会被限流的。
image.pngimage.png

但是呢,有个问题,如果按照我的版本来进行学习的话,这个限流是失败的,根本没有上述的限流效果,这是为什么呢?
从 1.6.3 版本开始,Sentinel Web filter 默认收敛所有 URL 的入口 context,因此链路限流不生效。1.7.0 版本开始(对应 SCA 2.1.1.RELEASE),我们在 CommonFilter 引入了 WEB_CONTEXT_UNIFY 这个 init parameter,用于控制是否收敛 context(入口资源关闭是否聚合)。将其配置为 false 即可根据不同的 URL 进行链路限流。

我们需要进行以下配置(记得重启):
添加一个pom依赖并添加一个配置类,然后重新配置限流就可以了

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-web-servlet</artifactId>
        </dependency>
@Configuration
public class FilterContextConfig {
    @Bean
    public FilterRegistrationBean sentinelFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new CommonFilter());
        registration.addUrlPatterns("/*");
        // 入口资源关闭聚合
        registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
        registration.setName("sentinelFilter");
        registration.setOrder(1);
        return registration;
    }
}

我们再看一下配置前和配置后的控制台差别:
配置前:
image.png
配置后:
image.png
我们可以明显看出来,配置了入口资源关闭聚合之后,sayHello不会再合并了,每一个接口都有一份了。
再有,也可以观察到这样的一个现象:你调用 /hello/123 的时候,只有 /hello/123 下面的 sayHello 的分钟通过会增加1,而 /hello2/123 下面的 sayHello 是不会增加的。

流控效果

流控模式分为三种,快速失败、Warm Up、排队等待(上面也说了)。

  • 快速失败:直接抛异常
  • Warm Up:根据coldFactor(冷加载因子,默认3)的值,从阈值/coldFactor,经过预热时长,才达到设置的QPS阈值
  • 排队等待:匀速排队,让请求以匀速通过,阈值类型必须设置为QPS,否则无效

快速失败

默认的流控效果。
快速失败就是在我们达到阈值的时候直接抛异常。我们之前的代码、案例都是使用的快速失败模式。

Warm Up

官方描述:https://github.com/alibaba/Sentinel/wiki/流量控制#warm-up
我们先对 /hello/{name} 接口进行一个WarmUp效果的限流配置。
image.png
此配置的含义:单机阈值6,预热时长3
在我们刚开始ide时候它会根据 阈值/coldFactor 规则进行流控,因为coldFactor默认是3,也就是说刚刚开始的时候,阈值是6/3=2,直到预热时长(3秒)时间达到了之后,才会将阈值提升到6,其过程是逐步提升的。
配置之后我们可以开始访问接口 /hello/{name} 进行测试。刚开始的3秒内,我们如果一秒内访问超过2次,就会看到限流现象,在3秒之后,我们一秒内访问超过6次才会产生限流现象(一秒6次用手动刷新挺费劲,所以一般看到的都是无论我怎么访问都没有限流,因为手动没有达到一秒6次访问啊,如果非要看到限流效果,找个工具比如jmeter进行访问)。

匀速排队

官方描述:https://github.com/alibaba/Sentinel/wiki/流量控制#匀速排队
简单描述下就是:无论你来多少个请求,我每秒只处理阈值量的请求,其余多的请求我也不报错,都在后面拍着,等前面的请求处理完之后再进行处理。
匀速排队只允许阈值类型是QPS类型,不支持线程数类型,利用的是漏桶算法

为了展示排队效果,我们将代码中添加一句打印语句:
image.png
控制台限流配置:
image.png
然后我们开始进行测试,疯狂访问 http://localhost:9031/hello/12323 然后观察控制台内容。我们会发现,虽然我们是连着疯狂请求的,但是控制台是每隔一秒打印一句,说明了他是每一秒执行一次请求。
image.png

那下面的超时时间是干嘛的?
我们可以想象成所有的请求都是放在了一个队列里等待执行,如果我队列里的任务超出了设置的等待时间,那么这个请求就会超时报错(超时时间是以毫秒为单位)。

这个就不演示了,我懒了……

熔断降级

官方文档:https://sentinelguard.io/zh-cn/docs/circuit-breaking.html
Sentinel的服务熔断、降级和Hystrix的熔断、降级是很像的。
旧版本的Sentinel的熔断是没有半开状态的(Hystrix有),但是!新版1.8的有!!!
根据新建降级规则页面内容,我们可以了解到,Sentinel的熔断策略一共有三个:慢调用比例(也就是旧版本的RT)、异常比例、异常数
image.png
Sentinel 提供以下几种熔断策略:

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

慢比例调用

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

image.png
在这里我们可以看到,慢比例调用有5个配置值

  • 最大RT: 单位毫秒,最大的响应时间,超过该值则记为慢调用
  • 比例阈值: 0到1,就是达到多少之后会进行熔断
  • 熔断时长: 单位秒,经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 最小请求数: 触发熔断的最小请求数目,若当前统计窗口内的请求数小于此值,即使达到熔断条件规则也不会触发
  • 统计时长: 单位毫秒,就是判断熔断时的单位时间

总体来说就是:在统计时长时间内,某个接口的请求数大于最小请求数、请求响应时间大于最大RT的请求比例大于比例阈值的时候会进行熔断。熔断的时间为熔断时长,在熔断时长达到后,会尝试进入半开状态,如果下一个请求时长大于RT继续熔断,小于则恢复正常。

然后我们进行一下配置并尝试。
此配置表示:一秒内 /hello/{name} 接口的请求数超过2、并且有一半的请求超过了800毫秒就进行熔断,熔断时间为2秒。
image.png
然后为了保证接口请求时长超过800毫秒,我们改造下代码,让它sleep1秒。
image.png
然后我们疯狂访问会发现确实被熔断了
image.png
在熔断期间2秒内我们再次请求发现还是被熔断的,然后我们等待2秒,再次请求会发现熔断结束了。
image.png

但是吧,我发现他这里有个Bug,就是我新建的时候我的比例阈值是0.5,可是当我编辑的时候,它回显的是1。
还有统计时长也是,无论我设置的是多少,编辑的时候都是1000。

异常比例

当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

让我们看一下这个配置页面:
统计时长内,如果接口收到的请求大于最小请求数,并且出错的请求比例大于比例阈值,那么就会进行熔断,熔断时间为熔断时长

我们来演示一下这个效果:
Hello/{name} 接口进行修改,让他每次都报错,然后重启:

比如我设置比例阈值为1,最小请求数为2,熔断时间3秒,统计时间1秒。
那么就会在1秒内接收到两次以上请求,并且请求都报错的时候进行熔断,熔断3秒后恢复。

之后我们访问 http://localhost:9031/hello/12323 ,结果如下:

  • 一秒内访问次数小于等于2次的的时候,页面只会报错,并没有进行熔断。

  • 一秒内访问次数大于2次的时候,页面开始从报错页面转变成了熔断页面。

  • 当熔断触发的时候,我继续访问还是会一直熔断(展示熔断页面),当我停止访问3秒后,再次进行访问,发现熔断结束了(展示报错页面)。

异常数

当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

其实异常数和异常比例很像,只不过是从比例转变成了具体的数量了而已。
如果直接一看就懂了就不需要看这一段的内容了,直接跳过就行了,我写只是为了文档的完整。

熔断效果展示:

  1. 删除之前的熔断规则,保证环境的干净,不会被其他规则影响
  2. 使用异常比例熔断规则的代码就可以。
  3. 配置熔断规则

  1. 验证
  • 在一秒内如果访问次数超过2次,异常次数超过1次,进行熔断,熔断时间3秒。
  • 经过3秒后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

热点参数限流

根据前面的讲述,限流规则肯定已经比较熟悉了。然而,在Sentinel中,还有一种单拿出来的一种限流,也就是此部分的热点参数限流
相比之下,热点参数限流就是在普通的限流功能上,增加了对于指定参数的限流功能,也是一个比较实用的限流功能。
描述一个功能场景:比如我们有一个查询商品接口,这个接口我们会经常访问,访问量特别大,我们需要进行限流(比如一秒内只能查询100次)。但是里面有几个特殊的商品,我们需要查询它的时候能够有一些特例,比如id为6(也就是指定对应特例参数的值)的数据可以一秒钟内查询100000次,这样的时候我们就可以用到热点参数限流功能。如果觉得这么描述还是不容易理解,我又举了个例子:
比如我有一个接口 /goods/findById?id= 经常会用到,并发量特别高,我需要对这个接口进行一个限流处理,但是id为6的商品是一个我收了广告费的商品,那我就需要在查询这个商品的时候提高查询时限流的阈值,这时候我就可以使用热点参数限流,对 /goods/findById 接口进行限流100,对id为6的特例限流100000。
我觉得这回应该是能理解了。

然后我们来看一下控制台配置页面:

然后我们解释一下配置信息的各个参数
基础配置:

  • 资源名:@SentinelResource 注解配置的值
  • 参数索引: 第几个索引,从0开始,以java代码中的参数为准,而不是url中的。它只关注你指定索引的参数,其他索引位置的参数他不管。
  • 单机阈值: 单位时间内达到的限流的访问量最大值
  • 统计窗口时长: 统计多久的时间内的请求量

高级选项:

  • 参数类型: 就是参数的类型。8个基本类型除去short、boolean再加上String,一共七种。
  • 参数值: 恩,就是这个参数的值。在值是这个值得时候,使用这个例外项的配置内容。
  • 限流阈值: 在请求的参数值符合配置的例外项的时候,使用此例外项对应的阈值。

验证:
首先我们把代码恢复到这样

然后启动项目,访问接口(因为控制台懒加载),进入控制台,配置热点参数限流。

然后我们访问该接口,一秒内访问一次的时候没问题,一秒钟内访问超过两次的时候它就会进行限流。

然后我们添加一个参数例外项,值为abc的限流阈值修改为100(只影响值为100的,其他的值还是使用上面的1)。

再对 http://localhost:9031/hello/12323 进行访问,毫无疑问,限流效果还是1秒1个请求。
然后再访问 http://localhost:9031/hello/abc 无论我们怎么刷新页面(就相当于访问),都是展示正常的,不会因被限流展示报错页面。然后我们为了展示100确实控制到了,我是用jmeter进行访问。设置好地址 http://localhost:9031/hello/abc 访问量101,一口气全部发送。


执行之后我们可以看到,101个请求只有最后一个失败了,前100个都成功了,证明了我们这个100的限流特例生效了。

post请求的有点问题还没搞懂,对接口已经创建特例了,但是就是没生效,还是访问第二次就报错。


系统自适应限流

在配置页面中是 系统规则

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

  • 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

@SentinelResource注解就相当于Hystrix的@HystrixCommand注解。
@SentinelResource注释指示Sentinel资源的定义。
在之前我们就配置过@SentinelResource这个注解,主不过是不知道它是什么意思、有什么作用而已,这里我们就对它进行一个简单的讲解。

功能与参数

注解参数详细内容查看以下两个链接,这里只是简单介绍:

通过查看源码,我们可以看到该注解一共有这么多个属性:
image.png
然后我们对它一一进行解释。

  • value: Sentinel资源的名称,我们不仅可以通过url进行限流,也可以把此值作为资源名配置,一样可以限流。(下一段就说的这个)
  • entryType: 条目类型(入站或出站),默认为出站
  • resourceType: 资源的分类(类型)
  • blockHandler: 块异常函数的名称,默认为空
  • blockHandlerClass: 块处理程序所在的类不应提供多个类
    • 默认情况下, blockHandler与原始方法位于同一类中。 但是,如果某些方法共享相同的签名并打算设置相同的块处理程序,则用户可以设置存在块处理程序的类。 请注意,块处理程序方法必须是静态的。
  • fallback: 后备函数的名称,默认为空
  • defaultFallback: 默认后备方法的名称,默认为空
    • defaultFallback用作默认的通用后备方法。 它不应接受任何参数,并且返回类型应与原始方法兼容
  • fallbackClass: fallback方法所在的类(仅单个类)
    • 默认情况下, fallback与原始方法位于同一类中。 但是,如果某些方法共享相同的签名并打算设置相同的后备,则用户可以设置存在后备功能的类。 请注意,共享的后备方法必须是静态的。
  • exceptionsToTrace: 异常类的列表追查, Throwable默认
  • exceptionsToIgnore: 要忽略的异常类列表,默认情况下为空

相关问题解决方案

在之前的使用过程中,我们肯定会发现虽然功能是体现出来了,但是我们在实际开发中并不能只是做到这样就行了,我们还需要更丰富完善的功能,所以我们这里提出了几个比较重要的问题以及解决方案。

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

主要使用@SentinelResource注解的fallback、fallbackClass、blockHandler、blockHandlerClass四个注解实现异常和流控的兜底方案。

规则持久化

我们之前在控制台配置流控、降级规则时候,会发现一个问题,微服务重启了之后相应的规则配置就会自动消失,并没有进行保存,再次使用需要重新配置。这在生产环境上可是巨大的灾难啊,配置了几十个几百个,重启一下,没了,那不能行那不能行,那我们要怎么搞?
我们可以将配置保存在MySQL、nacos……中,这里用nacos举例。
所有能保存的中间件以及保存方式查阅:https://github.com/alibaba/Sentinel/wiki/动态规则扩展

在pom文件中添加如下依赖

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

application.yml中添加:
在nacos的控制台添加如下配置:

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

image.png
字段含义:
image.png
然后,我们访问一次接口之后,可以发现,在Sentinel控制台的流控规则中可以看到我们刚在nacos上配置的规则。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值