Spring Cloud Alibaba Sentinel实现熔断与限流 , Sentinel 是面向微服务的轻量级流量控制框架,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。  下面是我个人学习Sentinel基础使用的学习笔记。

1. Sentinel控制台下载使用

sentinel分为两个部分:

  1. 核心库(Java客户端)不依赖任何框架/库,能够运行与所有Java运行时环境,对Dubbo /Spring Cloud也能够很好的支持。
  2. 控制台(Dashboard)基于Spring Boot开发,打包后可以直接运行,不需要Tomcat的支持.

1.1 下载jar包

去GitHub地址: https://github.com/alibaba/Sentinel/releases 下载控制台对应的jar包

1.2 运行jar包

java -jar sentinel-dashboard-1.7.2.jar

1.3 Web界面测试

测试访问Web界面:localhost:8080

默认用户名和密码都是sentinel,登录之后:

补充一下,也可以使用docker来安装

docker pull bladex/sentinel-dashboard

运行

docker run --name sentinel -d -p 8858:8858 -d bladex/sentinel-dashboard

访问

2. 初始化演示工程

2.1 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>live.yremp.springcloud</artifactId>
        <groupId>live.yremp</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-alibaba-sentinel-service8401</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </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>
    </dependencies>
</project>

2.2 配置文件

server:
  port: 8401

spring:
  application:
    name: cloud-alibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: 39.105.173.178:8848 #配置Nacos地址
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719

management:
  endpoints:
    web:
      exposure:
        include: '*'

2.3 主启动类

package live.yremp.springcloud.alibaba;

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

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

2.4 业务代码

业务代码里面主要就是

package live.yremp.springcloud.alibaba.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestSentinelController {
    @GetMapping("/testA")
    public String testA(){
        return "------------------Test-A";
    }
    @GetMapping("/testB")
    public String testB(){
        return "------------------Test-B";
    }
}

2.5 启动测试监控

启动项目,先去Nacos查看服务注册情况

然后去Sentinel Dashboard查看监控情况

发现并没有任何服务,需要我们手动请求一下 cloud-alibaba-sentinel-service8401 的两个测试接口

再去Sentinel Dashboard查看

此时可以看到我们测试监控的服务了

3.流控规则

3.1 流控模式

  • 直接:API达到限流条件时直接限流
  • 关联:当关联的资源达到阈值时,就限流自己
  • 链路:只记录指定链路上的流量

4 QPS直接快速失败

对TestA接口进行限流,QPS(每秒请求数)设置为1,超过这个阈值就会快速失败

快速刷新浏览器,就会触发这个流控规则

3.1.2线程数直接快速失败

修改Controller,使请求先睡眠1S然后再继续执行,这样主要是容易观察到

package live.yremp.springcloud.alibaba.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestSentinelController {
    @GetMapping("/testA")
    public String testA() throws InterruptedException {
        Thread.sleep(1000);
        return "------------------Test-A";
    }
    @GetMapping("/testB")
    public String testB(){
        return "------------------Test-B";
    }
}

重新启动项目,开两个窗口进行测试

后面的请求就会被限流,直接返回失败信息

3.1.2 关联模式

关联模式,当关联的资源的达到阈值就限流自己,下面设置A的限流规则,关联/testB ,当 testB服务请求过多,/testA就会被限流。

使用Postman对testB进行模拟并发访问

此时testB会被并发请求,并且超过了它的阈值1,访问testA就会触发限流规则

3.2 流控效果

3.2.1 快速失败

这个就是触发对应规则直接返回异常,例如:

此图像的alt属性为空

3.2.2 预热

Warm Up:根据codeFactor (冷加载因子,默认3)的值,从阈值/codeFactor, 经过预热时长,才达到设置的QPS阈值。

对testB增加一个预热流控效果,如下图

10s内 阈值会从默认的3慢慢增加到10,我们进行测试:

刚开始刷新会出现限流的信息,但是随着预热阈值不断增加,后面几乎不会再出现这种限流信息了。

3.2.3 排队

排队就是指定阈值,如果有超出阈值的请求可以进行排队等待处理,如果超出超时时间请求就会自动放弃。

每秒只处理一个请求,其他的请求经行排队,超时快速失败。

3.3 服务降级

3.3.1 服务降级RT

修改Controller,增加一个testC方法

@GetMapping("/testC")
    public String testC() throws InterruptedException {
        TimeUnit.SECONDS.sleep(1);
        System.out.println("testC is done!");
        return "------------------Test-C";
    }

RT是指平均响应时间(秒级),如下图,200ms内处理完请求,如果不能处理完成 且QPS大于5 ,在接下来的1s内进行服务降级快速失败

使用jmeter进行测压,10个线程无限循环

请求 http://localhost:8401/testC

然后浏览器正常请求 testC,发现服务被降级了

关闭Jmeter再次测试,发现又可以正常访问

Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高) ,对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。

当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。

3.3.2 服务降级之异常比例

异常比例( DEGRADE_ GRADE_ EXCEPTION RATIO ):当资源的每秒请求量>= 5,并且每秒异常总数占通过量的比值超过阈值( DegradeRule中的count )之后,资源进入降级状态,即在接下的时间窗口( DegradeRule 中的timeWindow, 以s为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0, 1.0], 代表0% - 100%。

Controller新增一个请求接口,如下:

@GetMapping("/testD")
    public String testD() throws InterruptedException {
        System.out.println("testD 异常比例测试!");
        int x =10/0; //故意写一个异常
        return "------------------Test-D";
    }

添加上面的异常比例降级规则,然后使用Jmeter进行压测模拟并发请求

此时浏览器访问testD

此时由于我们故意设置的异常(100%异常),会导致服务降级。如果我们停掉Jmeter,然后去浏览器访问就会直接显示异常信息(因为QPS没有5)

3.3.2 服务降级之异常数

异常数( DEGRADE GRADE EXCEPTION_ COUNT ):当资源近1分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若timeWindow 小于60s,则结束熔断状态后仍可能再进入熔断状态。

4. 热点Key限流

4.1 基础使用

Controller里面添加下面的内容

@GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "del_HotKey")
    public String testHotkey(@RequestParam(value = "p1",required = false) String p1,
                             @RequestParam(value = "p2",required = false) String p2){
        return "test HotKey";

    }

    public String del_HotKey(String p1,String p2, BlockException e){
        return "deal_testHotKey";
    }

添加下面的热点规则,testHotKey是对应@SenntinelResource注解里面的value

测试一下,刚开始按照1s一次的频率请求:

快速请求

如果修改Controller里面的代码,去掉blockHandler = "del_HotKey"

@GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey")
    public String testHotkey(@RequestParam(value = "p1",required = false) String p1,
                             @RequestParam(value = "p2",required = false) String p2){
        return "test HotKey";

    }

    public String del_HotKey(String p1,String p2, BlockException e){
        return "deal_testHotKey";
    }

我们去触发一下热点Key

4.2 参数额外项

我对testhotKey 的第一个参数p1 进行了额外的项配置,当p1的值为1时阈值就可以额外设置为200

即使以很快的速度刷新请求(实际上肯定到不了200QPS)也是正常访问,而我将p1的值设置为2,再次测试则会被Sentinel限流拦截。

5. 系统规则

我新建一个系统规则设置入口QPS为1,即所有接口的访问都会限制1QPS

6. SentinelResource

下面是对@SentinelResource注解的一些使用详解,还是先在Controller里面添加下面的方法 blockHandler 相当于异常之后的兜底方法

6.1 按资源名称限流

@GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource(){
        return new CommonResult(200,"按资源名称测试OK",new Payment(2000L,"serial007"));
    }

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

测试服务可用

新增1QPS的流控规则

快速刷新,测试流控规则是否生效

为什么说是按资源名称?因为实际上在sentinel后台会有两个链路,如下:

其中有一个/byResource 这个是按URL的,byResource 是按资源名称

6.2 按URL限流

在Controller里面再添加一个请求接口,没有设置兜底方法:

@GetMapping("/byUrl")
    @SentinelResource(value = "byUrl")
    public CommonResult byUrl(){
        return new CommonResult(200,"按资URL测试OK",new Payment(2000L,"serial007"));
    }

重启测试

按URL新增流控规则

快速刷新,测试流控规则

因为没有设置兜底方法,所以提示信息是Sentinel默认的提示

6.3 自定义限流处理类

上面每个方法都对应一个 blockHandler 即一个兜底方法,把异常处理的代码和业务代码耦合在一起,这明显不合适,我们可以自定义 blockHandler 来解耦。

先新建一个自定义的限流处理类

package live.yremp.springcloud.alibaba.handler;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import live.yremp.springcloud.entries.CommonResult;

public class CustomerBlockHandler {
    public static CommonResult handleException1(BlockException exception){
        return new CommonResult(444,",自定义 Global blockHandler --->1");
    }

    public static CommonResult handleException2(BlockException exception){
        return new CommonResult(444,",自定义 Global blockHandler --->2");
    }
}

在Controller里面新建下面的方法并配置我们自定义的限流处理类和方法

@GetMapping("/customerBlockHanlder")
    @SentinelResource(value = "customerBlockHanlder",
                      blockHandlerClass = CustomerBlockHandler.class,
                      blockHandler = "handleException2")
    public CommonResult customerBlockHanlder() {
        return new CommonResult(200, "customerBlockHanlder测试OK", new Payment(2000L, "serial007"));
    }

测试正常访问情况

添加限流规则测试我们自定义的限流处理类里面的方法

添加之后快速刷新浏览器触发限流规则

标签云

ajax AOP Bootstrap cdn Chevereto CSS Docker Editormd GC Github Hexo IDEA JavaScript jsDeliver JS樱花特效 JVM Linux Live2D markdown Maven MyBatis MyBatis-plus MySQL Navicat Oracle Pictures QQ Sakura SEO Spring Boot Spring Cloud Spring Cloud Alibaba SpringMVC Thymeleaf Vue Web WebSocket Wechat Social WordPress Yoast SEO 代理 分页 图床 小幸运 通信原理

Spring Cloud Alibaba Sentinel实现熔断与限流
Spring Cloud Alibaba Sentinel实现熔断与限流