(第十五篇)spring cloud之Sentinel实现熔断与限流

一、概述

1、介绍

Sentinel("哨兵")是阿里巴巴开源分布式系统流量治理组件 ,被誉为 "分布式系统的流量防卫兵"。它以流量为核心切入点,从流量控制、熔断降级、系统负载保护、热点参数防护等多维度保障微服务架构的稳定性,已成为国内微服务生态中高可用保障的主流方案之一。

官方网址https://sentinelguard.io/zh-cn/GitHub文档地址https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

2、功能

功能模块 核心能力 典型应用
流量控制 限制 QPS / 并发数,控制入口流量,削峰填谷 秒杀系统、高并发接口限流
熔断降级 依赖服务异常时快速熔断,降级返回兜底逻辑,避免级联故障 第三方接口超时 / 异常容错
系统自适应保护 基于系统负载(CPU/Load/QPS)动态限流,防止系统过载 高并发场景系统稳定性保障
热点参数限流 针对热点参数(如商品 ID、用户 ID)精准限流,防护热点击穿 热门商品、大促活动场景
来源访问控制 基于调用来源(IP / 应用)进行权限与流量控制 内外网接口隔离、白名单控制
实时监控 秒级统计流量、规则命中、系统指标,支持可视化与告警 运维监控、故障排查
规则动态配置 支持规则动态下发与热更新,无需重启服务 线上流量策略灵活调整

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

二、安装

  • 核心库(Java 客户端):轻量级依赖,无框架限制,负责资源定义、流量统计、规则校验与执行。
  • 控制台(Dashboard) :可视化管理工具,负责规则配置、监控展示、机器管理、规则持久化 ,默认账号 / 密码为sentinel/sentinel

1、下载

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

2、运行

需要Java8环境,注意8080端口不要占用

java -jar sentinel-dashboard-1.8.9.jar

或者指定端口

java -Dserver.port=8081 -jar sentinel-dashboard-1.8.9.jar

3、访问

三、限流降级规则

1、工程搭建

(1)创建工程

(2)添加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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.hk.cloudstudy</groupId>
        <artifactId>SecondSpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.hk</groupId>
    <artifactId>cloud-sentinel-server8401</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </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)配置文件

XML 复制代码
server:
  port: 8401

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

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

(4)启动类

java 复制代码
@SpringBootApplication
@EnableDiscoveryClient
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

(5)业务类

java 复制代码
@RestController
public class FlowLimitController
{

    @GetMapping("/testA")
    public String testA()
    {
        return "------testA";
    }

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

(6)启动

先启动nacos和sentinel,再启动8401服务,第一次查看sentinel是空的,应为sentinel是懒加载,当访问接口后就会出现监控的控制台

2、流控规则

(1)名词解释

  • 资源名:唯一名称,默认请求路径
  • 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)
  • 阈值类型/单机阈值:
    • QPS(每秒钟的请求数量):当调用该api的QPS达到阈值的时候,进行限流
    • 线程数:当调用该api的线程数达到阈值的时候,进行限流
  • 是否集群:不需要集群
  • 流控模式:
    • 直接:api达到限流条件时,直接限流
    • 关联:当关联的资源达到阈值时,就限流自己
    • 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)[api级别的针对来源】
  • 流控效果:
    • 快速失败:直接失败,抛异常
    • Warm Up:根据codeFactor (冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
    • 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效

(2)流控模式

1)直接模式

说明:对/testA资源的访问每秒查询1次,超过1次直接快速失败,抛出默认异常

测试:每秒访问1次是正常的,超过1次出现 Blocked by Sentinel (flow limiting) 错误

2)关联模式

说明:当关联资源/testB达到阀值时,就限流/testA的访问。

一直访问B,同时访问A时出现错误

3)链路模式

testB访问testA, testC也访问testA,那个只会限制testB的访问,不会限制testC的访问

例子:有一个公共方法:goodsService.getStock()

它被两个入口调用:

  1. 入口 A:/api/h5/detail(H5 页面,高并发)
  2. 入口 B:/api/admin/stock(后台管理,低流量)

只想限制 H5 页面带来的流量,不想影响后台。就可以给 getStock() 设置:

  • 流控模式:链路
  • 入口资源:/api/h5/detail

只有从 H5 过来的请求会计数并限流,后台调用不受影响。

从指定入口链路进来的流量 → 达到阈值 → 限流

(3)流控效果

1)快速失败

说明:对/testA资源的访问每秒查询1次,超过1次直接快速失败,抛出默认异常

测试:每秒访问1次是正常的,超过1次出现 Blocked by Sentinel (flow limiting) 错误

2)WarmUp预热

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

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

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

3)排队等待

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

/testA每秒1次请求,超过的话就排队等待,等待的超时时间为2000毫秒。

3、降级规则

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

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

(1)名词解释

RT(平均响应时间,秒级)

平均响应时间超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级

窗口期过后关闭断路器

RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

异常比列(秒级)

QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

异常数(分钟级)

异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

(2)降级策略

1)RT

平均响应时间(DEGRADE_GRADE_RT):当1s内持续进入5个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以ms为单位),那么在接下的时间窗口(DegradeRule中的timeWindow,以s为单位)之内,对这这个方法的调用都会自动地熔断 (抛出 DegradeException) .

注意Sentinel默认统计的 RT上是4900ms,超出此阈值的都会算作4900ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置。

调用/testC,如果超过200毫秒还没处理完,在未来3秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用。3秒后缓慢请求服务会逐渐可用

testC代码:

java 复制代码
    @GetMapping("/testC")
    public String testC()
    {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "------testC";
    }
2)异常比例

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

每秒请求数大于5,异常数占总请求数的的50%,则服务降级,3秒后如何服务恢复则请求慢慢放开

java 复制代码
    @GetMapping("/testC")
    public String testC()
    {
        int i = 1/0;
        return "------testC";
    }

JMeter关闭后,服务将慢慢恢复,不在降级,而是报错

3)异常数

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

时间窗口一定要大于等于60秒。

前面5次都是出现error,第6次出现降级错误

4、热点规则

是一种参数级细粒度限流 机制,核心作用是:识别并限制高频访问的 "热点参数值"(如爆款商品 ID、高频用户 ID),防止热点数据打垮缓存或数据库,避免 "缓存击穿 / 穿透 / 雪崩" 等问题。

参数 含义 说明
资源名 要限流的方法 / 接口 必须与 @SentinelResource 的 value 一致
参数索引 限流的参数位置 从 0 开始计数,第一个参数为 0
单机阈值 单个参数值的默认阈值 所有未配置例外项的参数值共享此阈值
统计窗口时长 统计时间范围 单位:秒,默认 1 秒

限流出问题后,都是用sentinel系统默认的提示:Blocked by Sentinel (flow limiting),也可以自定义提示。

1)参所索引限流

对资源/testD的第二个参数进行限流,每秒不能操作3个请求,超过返回"活动过于火爆的提示"。

java 复制代码
    @GetMapping("/testD")
    @SentinelResource(value = "testD", blockHandler = "blockHandler_testD")
    public String testD(@RequestParam String name, @RequestParam String sex)
    {
        return "------testD" + name + ":" + sex;
    }

    public String blockHandler_testD(String name, String sex, BlockException exception){
        return "活动过于火爆。。。";
    }

@SentinelResource注解的方法参数索引,0代表第一个参数,1代表第二个参数,以此类推

单机阀值以及统计窗口时长表示在此窗口时间超过阀值就限流。限流后调用blockHandler_testD方法。

2)特殊值限流

上述案例演示了第二个参数当QPS超过1秒3次点击后被限流,但是如果期望name参数当它是某个特殊值时对他进行限流

例如:对name参数为lisi,每秒超过1次点击进行限流,其他值的时候每秒超过3次限流

localhost:8401/testD?name=lisi&sex=f 每秒超过1次进行限流

localhost:8401/testD?name=王五&sex=f 每秒超过3次进行限流

热点参数的注意点,参数必须是基本类型或者String

5、系统规则

是一种全局级别的系统保护机制 ,核心作用是:从系统整体负载出发,监控 CPU、内存、RT、入口 QPS、线程数等核心指标,当系统接近或超过承载能力时,自动触发保护,防止系统被压垮。

参数 含义 取值范围 说明
CPU 使用率 系统 CPU 使用率阈值 0.0~1.0(1.5.0 + 版本支持) 超过阈值触发保护
平均 RT 所有入口流量的平均响应时间 ≥0(ms) 平均 RT 达到阈值触发保护
入口 QPS 应用所有入口的总 QPS ≥0 总 QPS 超过阈值触发保护
线程数 应用所有入口的总线程数 ≥0 总线程数超过阈值触发保护
load 系统 1 分钟负载(仅 Linux 支持) ≥0 load1 超过阈值触发保护

四、@SentinelResource注解

1、资源名称限流

(1)编写代码

java 复制代码
@RestController
public class RateLimitController
{
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public String byResource() {
        return "访问成功 OK";
    }
    public String handleException(BlockException exception)
    {
        return "服务不可用";
    }
}

(2)限流配置

(3)测试

1秒访问一次

超过1秒1次

问题:如果将服务8401关闭,则配置的流控规则将都会消失

2、url地址限流

(1)编写代码

java 复制代码
    @GetMapping("/byUrl")
    public String byUrl()
    {
        return "访问成功 OK";
    }

(2)配置

(3)测试

超过阈值会出现默认错误

3、自定义限流逻辑

(1)创建自定义限流处理类

java 复制代码
public class MyBlockHandler {
    public static String handleException(BlockException exception){
        return "自定义的限流处理信息......MyBlockHandler";
    }

}

(2)业务类

java 复制代码
    @GetMapping("/myBlockHandler")
    @SentinelResource(value = "myBlockHandler", blockHandlerClass = MyBlockHandler.class, blockHandler = "handleException")
    public String customerBlockHandler() {
        return "访问成功 OK";
    }

(3)配置

(4)测试

超过阈值返回自定义阈值

4、注解属性说明

java 复制代码
@SentinelResource(
    value = "资源名",         // 【必填】唯一资源名称
    blockHandler = "限流兜底方法",  // 流控/热点/系统规则触发的兜底
    fallback = "熔断兜底方法",      // 熔断/业务异常触发的兜底
    exceptionsToIgnore = {}   // 忽略的异常,不走降级
)
参数 作用 触发场景 方法要求
value 资源名称(必填) 控制台配置规则用 唯一标识,建议见名知意
blockHandler 限流 / 热点 / 系统规则兜底 流控、热点、系统保护触发 参数 = 原方法 +BlockException
fallback 熔断 / 业务异常兜底 慢调用、异常、业务报错 参数 = 原方法 +Throwable
defaultFallback 默认通用兜底 所有异常 / 熔断通用 无参方法
exceptionsToIgnore 忽略异常 指定异常不走 fallback 异常类数组

注意点:

1、兜底方法参数

blockHandler = 原方法参数 + BlockException

fallback = 原方法参数 + Throwable

2、权限

必须是public,全局兜底必须是 static

3、返回值

必须和原方法的返回值保持一致

五、熔断服务

1、Ribbon系列

(1)服务提供者

例如:服务提供者84提供了一个接口

java 复制代码
    @GetMapping(value = "/getData/{id}")
    public String getData(@PathVariable("id") Long id)
    {
        return "我是服务提供者84:" + id;
    }

(2)服务消费者

1)配置类RestTemplate

java 复制代码
@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

2)配置fallback和blockHandler

java 复制代码
@RestController
@Slf4j
public class MyBreakerController {
    
    // 服务提供者名称
    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/fallback/{id}")
     @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")
    public String fallback(@PathVariable Long id){
        String result = restTemplate.getForObject(SERVICE_URL + "/getData/"+id,String.class,id);
        if (id == 4) {
            throw new IllegalArgumentException ("非法参数异常....");
        }else if (result.getData() == null) {
            throw new NullPointerException ("NullPointerException,该ID没有对应记录");
        }
        return result;
    }

    public String handlerFallback(@PathVariable  Long id,Throwable e) {
        
        return "可能不存在:" + id);
    }

    public String blockHandler(@PathVariable  Long id,BlockException blockException) {
       
        return "blockHandler-sentinel限流, id: " + id);
    }

}

若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。

2、Feign系列

(1)服务提供者

例如:服务提供者84提供了一个接口

java 复制代码
    @GetMapping(value = "/getData/{id}")
    public String getData(@PathVariable("id") Long id)
    {
        return "我是服务提供者84:" + id;
    }

(2)服务消费者

1)添加pom

XML 复制代码
        <!--SpringCloud openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

2)配置文件

XML 复制代码
server:
  port: 84

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

management:
  endpoints:
    web:
      exposure:
        include: '*'
# 激活Sentinel对Feign的支持
feign:
  sentinel:
    enabled: true  

3)@FeignClient注解的业务接口

java 复制代码
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService
{
    @GetMapping(value = "/getData/{id}")
    public String getData(@PathVariable("id") Long id);
}

4)服务降级实现类

java 复制代码
@Component
public class PaymentFallbackService implements PaymentService {
    @Override
    public String getData(Long id){
        return "服务降级返回,id: " + id;
    }
}

5)业务类

java 复制代码
@RestController
@Slf4j
public class CircleBreakerController {

    @Resource
    private PaymentService paymentService;

    @GetMapping(value = "/consumer/openfeign/{id}")
    public String paymentSQL(@PathVariable("id") Long id)
    {
        if(id == 4) {
            throw new RuntimeException("没有该id");
        }
        return paymentService.paymentSQL(id);
    }

}
 

6)启动类

添加@EnableFeignClients启动Feign的功能

java 复制代码
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class OrderNacosMain84 {
    public static void main(String[] args) {
            SpringApplication.run(OrderNacosMain84.class, args);
    }
}

7)测试

测试84调用服务提供者,故意关闭微服务提供者,看84消费侧自动降级,不会被耗死

六、配置持久化

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

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

(1)修改工程

(2)修改pom文件

XML 复制代码
<!--SpringCloud ailibaba sentinel-datasource-nacos -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

(3)修改yml配置文件

添加Nacos数据源配置

spring:

cloud:

sentinel:

datasource:

ds1:

nacos:

server-addr: localhost:8848

dataId: ${spring.application.name}

groupId: DEFAULT_GROUP

data-type: json

rule-type: flow

XML 复制代码
server:
  port: 8401

spring:
  application:
    name: cloud-sentinel-service
  cloud:
    nacos:
      discovery:
        #Nacos服务注册中心地址
        server-addr: hd01:8848
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        dashboard: hd01:8081
        #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: hd01:8848
            dataId: cloud-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

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

(4)添加Nacos业务规则配置

resource:资源名称;

limitApp:来源应用;

grade:阈值类型,0表示线程数,1表示QPS;

count:单机阈值;

strategy:流控模式,0表示直接,1表示关联,2表示链路;

controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;

clusterMode:是否集群。

(5)测试

重启cloud-sentinel-service,查看规则是否存在,发现规则依然存在

相关推荐
snow@li2 小时前
数据库-Oracle:常用语法 / Oracle 核心知识技能梳理
数据库·redis·缓存
qq_392690662 小时前
如何处理MongoDB分片集群的连接池耗尽危机_客户端连接与mongos到shard的连接乘数效应
jvm·数据库·python
叶小鸡2 小时前
Java 篇-项目实战-天机学堂(从0到1)-day8
数据库·oracle
qq_372154232 小时前
Python异步爬虫如何应对封IP_结合asyncio与代理池实现轮询请求
jvm·数据库·python
abc123456sdggfd2 小时前
php怎么处理跨域请求_php如何设置header解决跨域问题详解
jvm·数据库·python
zhangchaoxies2 小时前
如何在CSS中正确加载本地JPG背景图片
jvm·数据库·python
阿坤带你走近大数据2 小时前
Oracle报错-锁问题
数据库·oracle
旺仔小拳头..2 小时前
JDBC 基础: API、SQL 注入问题,事务、连接池
数据库·sql
NineData2 小时前
玖章算术NineData成功入选杭州市“新雏鹰”企业
运维·数据库·后端