SpringCloud:Sentinel实现熔断与限流(1)

Sentinel  /  安装Sentinel控制台  /  初始化演示工程     /      流控规则

Sentinel

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

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

Sentinel 是轻量级的流量控制、熔断降级Java库;功能类似于Hystrix

入门文档:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel

服务使用中的各种问题:服务雪崩、服务降级、服务熔断、服务限流

2.安装Sentinel控制台

Sentinel分为两个部分:

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

安装步骤

下载到本地sentinel-dashboard-1.8.2.jar
运行命令:
前提需要Java8,且8080端口不能被占用;java -jar sentinel-dashboard-1.8.2.jar

访问 localhost:8080,账号密码均为sentinel

3.初始化演示工程

3.1 启动Nacos8848

3.2 新建工程

3.2.1 新建Module

cloudalibaba-sentinel-service8401

3.2.2 pom

以后基本上nacos 跟sentinel一起配置

pom.xml

<?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>cloud6</artifactId>
        <groupId>com.tinstu.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-sentinel-service8401</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <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>

</project>

3.2.3yaml

spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互。
比如 Sentinel 控制台添加了1个限流规则,会把规则数据push给这个Http Server接收,Http Server再将规则注册到Sentinel中。
spring.cloud.sentinel.transport.port:指定与Sentinel控制台交互的端口,应用本地会启动一个占用该端口的Http Server

application.yaml

server:
  port: 8401

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

3.2.4主启动类

SentinelMainApp8401

package com.atguigu.cloudalibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class SentinelMainApp8401 {
    public static void main(String[] args) {
        SpringApplication.run(SentinelMainApp8401.class, args);
    }
}

3.2.5 业务类

FlowLimitController

package com.atguigu.cloudalibaba.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测试

启动Nacos8848,启动8401,启动Sentinel

查看Sentinel控制台,发现什么也没有

原因:Sentinel采用懒加载机制
执行一下:http://localhost:8401/testA

sentinel8080正在监控微服务8401

4.流控规则

流量限制控制规则,分为:流控模式和流控效果

4.1 流控模式

流控模式有三种:直接、关联、链路

4.1.1 直接(默认)+快速失败(默认)

(1) QPS直接快速失败

QPS:query per second,每秒钟的请求数量,当调用该api的QPS达到阈值时,进行限流。
下面设置表示1秒钟内查询一次就是OK,若QPS>1,就直接-快速失败,报默认错误

测试一下,当/testA的访问超过1次/s是,页面报错。被Sentinel限流,还能继续请求只要QPS<=1。

(2) 线程数直接快速失败

当调用该api的线程数达到阈值的时候,进行限流。
与QPS直接快速失败不同的是,QPS情况下限制的是流量,比如银行的人流量只能是1人/s,也就是说每次只能一个人进入银行办理业务;而线程数就好比银行只有一个窗口开放,一群人都可以进入银行,但是每次只能处理一个人的业务。

演示效果:
先修改一下8401的业务类

然后重启8401,测试/testA,最好用两个浏览器访问,效果更明显

4.1.2 关联

当关联的资源达到阈值时,就限流自己。比如当与A关联的资源B达到阈值后,就限流A自己。
例如:支付接口达到阈值,限流下订单的接口。

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

2. 测试
单独访问testB成功

postman模拟并发密集访问testB
先创建一个集合,名字自己随便取。

然后将创建的访问/testB的请求保存在创建的集合中

设定集合运行参数,20个线程,每次间隔0.3s访问一次(QPS>1),执行:

然后再访问/testA,发现被限流,等postman执行完毕,testA又可以访问了

4.1.3 链路

链路:只记录指定链路上的流量(指定资源人入口资源进来的流量,如果达到国值,就进行限流

4.2 流控效果

快速失败在上面的流控模式演示过了,他是默认的流控效果,直接失败,抛出异常。源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

4.2.1 warm up 预热

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

官网:https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8

源码:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值

测试

狂点请求,可以看通过的QPS逐渐增加,最开始会报错限流,之后就可以抗住10/s的QPS了

4.2.2 排队等待

官网:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

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

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

/testA的QPS最大为1,超过的话就排队等待,等待的超时时间为20000ms。

修改一下业务代码,把线程名打印出来以验证是否排队。

测试

5.降级规则(熔断规则)

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

Sentinel 提供以下几种焙断策略:

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

5.1慢调用比例,RT(平均响应时间,秒级)

官方繁琐概念(不如看例子)

老版本:

新版本:

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

实战测试

业务类中加一个rest 接口,以用于测试:

@GetMapping("/testD")
public String testD() {
    //暂停1秒
    try {
        TimeUnit.MILLISECONDS.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("testD  测试慢调用比例 RT");
    return "------tedtD";
}

编辑熔断规则:

在1000ms的统计时间内,总请求数(超过5次)中有80%的请求最大RT超过了200ms,那么触发熔断机制,熔断2s。

jmeter压测:

永远一秒钟打进来10个线程(大于5个了)调用testD,我们希望200毫秒处理完本次任务,如果超过200毫秒还没处理完,在未来s秒钟的时间内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了。
testD被熔断了

停止jmeter,没有这么大的访问量了,断路器半开到关闭(保险丝恢复),微服务恢复OK

5.1.2 异常比例

概念

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

实战测试

编辑熔断规则:

1000ms统计时长内,大于5次的请求中超过80%的请求出现异常,则熔断2s。

jmeter压测

单独访问一次,必然来一次报错一次(int age = 10/0),调一次错一次;开启jmeter后,直接高并发发送请求,多次调用达到我们的配置条件了。断路器开启(保险丝跳闸),微服务不可用了,不再报错error而是服务降级了

停掉jemeter后过2s。报/zero错误,因为业务类中有个10/0。

5.1.3 异常数

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

实战测试
修改业务类:

编辑熔断规则:

好像有bug,虽然熔断了但是熔断时长不是我配置的5s,大约是一分钟,统计时长也不是1s,好像也是一分钟,同时没达到最小请求数,只达到3次异常就直接熔断了。虽然我用的新版本,但是逻辑好像跟老版本的一样?

阅读剩余
THE END