SpringCloud Gateway 集成 Sentinel 详解 及实现动态监听Nacos规则配置实时更新流控规则

目录

一、前言

SentinelSpringCloud Alibaba 家族的服务保护组件,很多项目在前中期没有遇到流量突增不太注意服务保护的重要性,当流量突增打爆应用服务或数据库时束手无策,可以不配置流控规则,但是需要时一定可以热加载使用,本文会对集成Sentinel以及动态拉取Nacos配置规则实现热加载流控规则进行讲解。

官网地址

二、版本选择和适配

使用 SpringCloud Alibaba 家族组件,要注意一下版本兼容问题,避免出现一些奇怪的问题,这里会说明本文使用的各组件版本,以及 SpringCloud Alibaba 推荐的各版本适配。

2.1、本文使用各组件版本

部分组件对版本兼容要求其实没有那么高,比如Nacos,不一定要按照官方推荐版本,差几个小版本没有什么影响,我本地使用的一直是Nacos2.0.2,代码集成实现基本都一样。

JDK:1.8.0

Spring-Boot:2.3.12.RELEASE

Spring-Cloud:Hoxton.SR12

Spring-Cloud-Alibaba:2.2.9.RELEASE

Nacos:2.0.2

Sentinel:1.8.5

2.2、官方推荐版本

官方版本说明

三、部署sentinel-dashboard

3.1、下载 sentinel-dashboard jar包

这里使用sentinel-dashboard-1.8.5,这里提供两个下载地址,需要其它版本可以自行去github下载。
github下载地址
百度网盘地址

3.2、启动 sentinel-dashboard
复制代码
java -Dserver.port=8180 -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=123456 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.5.jar
  • -Dserver.port=8180
    sentine 服务控制台端口
  • -Dsentinel.dashboard.auth.username=sentinel
    sentine 控制台登录账号,不设置默认sentinel
  • -Dsentinel.dashboard.auth.password=123456
    sentine 控制台登录密码,不设置默认sentinel
  • -Dcsp.sentinel.dashboard.server=localhost:8180
    将控制台自身注册到server
  • -Dproject.name=sentinel-dashboard
    控制台服务自己项目名称


四、Gateway 集成 Sentinel实现控制台配置流控规则测试

4.1、添加Gateway 集成 Sentinel 包

在原有网关项目基础上添加上这两个包,这两个包会将gateway集成sentinel,并且默认是自动配置的,无需手动配置。

复制代码
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
4.2、添加 Gateway 服务启动JVM参数
4.2.1、配置说明

Gateway 连接 Sentinel 控制台的配置,Sentinel1.7.0 版本以下不支持配置文件配置,推荐直接使用JVM参数配置。

  • 添加JVM启动参数:

    -Dcsp.sentinel.dashboard.server=127.0.0.1:8180

    -Dcsp.sentinel.api.port=18719

    -Dproject.name=kerwin-gateway

    -Dcsp.sentinel.app.type=1

  • 参数说明:

    -Dcsp.sentinel.dashboard.server:指定控制台地址和端口。

    -Dproject.name:在sentinel控制台中展示的项目名称。

    -Dcsp.sentinel.api.port:指定客户端监控 API 的端口(默认是 8719),如控制台修改规则,则会向该端口推送规则信息。

    -Dcsp.sentinel.app.type:从 1.6.3 版本开始,控制台支持网关流控规则管理。该启动参数设置成1会将您的服务标记为 API Gateway,在接入控制台时您的服务会自动注册为网关类型,然后您即可在控制台配置网关规则和 API 分组。

4.2.2、启动说明

这里提供服务打成jar包启动和使用IDEA开发工具添加JVM参数启动示例。

4.2.2.1、使用 jar 包启动Gateway添加JVM启动参数
复制代码
java -Dcsp.sentinel.dashboard.server=127.0.0.1:8180 -Dcsp.sentinel.api.port=18719 -Dproject.name=kerwin-gateway -Dcsp.sentinel.app.type=1 -jar kerwin-gateway.jar
4.2.2.2、IDEA中配置JVM启动参数(IDEA版本2022.2.1)

不同版本IDEA可以配置入口不同,有需要可以自己查询


4.3、启动 Gateway 注册到 Sentinel-dashboard 实现接口流控规则动态配置

这里需要注意,Gateway 集成 Sentinel-dashboard 默认是懒加载的,需要调用一次接口才能注册到 Sentinel-dashboard,也可以直接在 Gateway 中配置成热加载,添加spring.cloud.sentinel.eager:true实现服务器启动了自动心跳注册。

4.3.1、启动 Gateway 注册到 Sentinel-dashboard
4.3.2、通过 Sentinel-dashboard 配置指定接口限流

这里需要注意,这个流控规则是按照组级别来的,一个组内所有匹配规则会共用一个阈值,如果需要在网关应用配置单独接口流控规则目前来看只能配置多个分组,然后单独配置规则。

  • 1、新增 API 分组

  • 2、自定义分组内 API 匹配规则

  • 3、新增网关流控规则

  • 4、配置网关流控规则(这里需要注意,这个流控规则是按照组级别来的,一个组内所有匹配规则共用一个阈值)

  • 5、测试限流规则

    快速请求两次可以看到服务端响应 Blocked by Sentinel: ParamFlowException,响应内容也是可以自定义的,这个会在后面说明。

PS1 秒内同时请求一次 /user/info 接口 和 /user/list 接口,也会响应Blocked by Sentinel: ParamFlowException,因为同一个分组共用一个阈值,如果要单独配置某一个接口目前看只能整多个分组。

4.4、注意事项

需要注意:如果不做特殊处理,通过Sentinel控制台配置的规则在应用服务重启后就没了,通过Sentinel控制台配置流控规则的本质其实就是将编辑好的规则加载到应用服务缓存中,并不会进行持久化,如果想要持久化Sentinel控制台配置的规则需要特殊处理,后续会进行说明。

五、Gateway 集成 Sentinel 常用配置

5.1、热加载
复制代码
spring:
  cloud:
    sentinel:
      # 设置sentinel为热加载 默认为false
      eager: true
5.2、降级处理配置(这里提供代码配置和使用配置文件配置)
5.2.1、通过代码配置(配置文件配置比代码配置优先级高)
复制代码
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;

@Configuration
public class SentinelGatewayConfiguration {

    /**
     * 自定义降级处理响应
     */
    @PostConstruct
    public void init(){
        GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable throwable) {
                return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).bodyValue("{"code":500,"msg":"代码配置-被限流了!"}");
            }
        });
    }
}
5.2.2、通过配置文件配置
复制代码
spring:
  cloud:
    sentinel:
      # 设置sentinel为热加载 默认为false
      eager: true
      scg:
        # 降级处理配置 也可以在代码中实现
        fallback:
          # 指定降级处理的模式为返回响应,也可以配置成重定向redirect,配置重定向需要指定重定向地址
          mode: 'response'
          response-status: 200
          response-body: '{"code":500,"msg":"配置文件配置-被限流了!"}'
          # mode 为 redirect 时使用
          redirect: 'https://blog.csdn.net/weixin_44606481'

六、自定义本地加载流控规则

因为 sentinel-dashboard 不会持久化手动配置的流控规则,一般情况下我们都会提前配置一些我们需要的规则,可以通过代码或者配置文件配置。

6.1、通过代码加载流控规则

Sentinel 的API管理存储在 GatewayApiDefinitionManager 类中,流控规则存储在 GatewayRuleManager 类中,添加好自己需要的配置即可。

复制代码
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.Set;

@Configuration
public class SentinelGatewayRuleConfiguration {

    @PostConstruct
    public void initRule(){
        // 加载根据路由 ID 配置限流规则
        this.initGatewayFlowRules();
        // 加载根据API分组配置限流规则
        this.initApiDefinitions();
    }

    private void initGatewayFlowRules() {
        // 存储限流规则的集合
        Set<GatewayFlowRule> rules = GatewayRuleManager.getRules();
        if(rules == null){
            rules = new HashSet<>();
        }
        // 根据路由 ID 配置限流规则
        GatewayFlowRule rule1 = new GatewayFlowRule("kerwin-user")
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)
                .setCount(1) // QPS阈值
                .setIntervalSec(1); // 间隔
        rules.add(rule1);
        // 加载限流规则
        GatewayRuleManager.loadRules(rules);
    }

    private void initApiDefinitions() {
        Set<ApiDefinition> apiDefinitions = GatewayApiDefinitionManager.getApiDefinitions();
        if(apiDefinitions == null){
            apiDefinitions = new HashSet<>();
        }
        // 创建一个 API 分组
        ApiDefinition apiDefinition1 = new ApiDefinition("user服务API组");
        // API 分组 URL 匹配规则
        Set<ApiPredicateItem> apiPathPredicateItems = new HashSet<>();
        // 添加精确匹配 匹配为 /api-user/user/info 的url
        apiPathPredicateItems.add(new ApiPathPredicateItem().setPattern("/api-user/user/info")
                        .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT));
        // 添加前缀匹配 用于匹配以 /api-user/user 开头的URL
        apiPathPredicateItems.add(new ApiPathPredicateItem().setPattern("/api-user/user/**")
                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
//        // 添加正则匹配 用于匹配以 list 结尾的 URL
//        apiPathPredicateItems.add(new ApiPathPredicateItem().setPattern("^.*list$")
//                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_REGEX));

        apiDefinition1.setPredicateItems(apiPathPredicateItems);
        apiDefinitions.add(apiDefinition1);

        // 根据 API 分组配置限流规则
        Set<GatewayFlowRule> rules = GatewayRuleManager.getRules();
        if(rules == null){
            rules = new HashSet<>();
        }
        GatewayFlowRule rule1 = new GatewayFlowRule(apiDefinition1.getApiName())
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                .setCount(1) // QPS阈值
                .setIntervalSec(1); // 间隔
        rules.add(rule1);

        // 加载 API 分组定义
        GatewayApiDefinitionManager.loadApiDefinitions(apiDefinitions);
        // 加载限流规则
        GatewayRuleManager.loadRules(rules);
    }
}

项目启动后可以在Sentinel控制台看到代码配置的API分组和流控规则。

6.2、通过本地配置文件加载流控规则

通过代码手动配置会比较麻烦而且不易调整,Gateway集成Sentinel包提供了通过配置文件加载API分组和流控规则实现,这里会将API分组和流控规则都分别写入不同的json文件中,交由对于实现类去进行加载。

6.2.1、API分组规则json文件编写(gateway-sentinel-api-groups.json)

resource目录创建gateway-sentinel-api-groups.json将API分组规则内容填进去。

复制代码
[
  {
    "apiName": "user服务API组",
    "predicateItems": [
      {
        "pattern": "/api-user/user/info",
        "matchStrategy": 0
      },
      {
        "pattern": "/api-user/user/**",
        "matchStrategy": 1
      }
    ]
  }
]

解释:

  • apiName:
    字符串,代表 API 组名称,这里是 "user服务API组",用于统一管理相关 API。
  • predicateItems:
    数组,包含多个 predicateItem 对象,用于描述 API 的匹配模式。
  • predicateItem 内部元素:
    • pattern:
      字符串,使用不同风格的表达式。
      例如,"/api-user/user/info" 精确匹配该路径;"/api-user/user/**" 匹配 /api-user/user/ 下的所有路径(包括子路径)。
    • matchStrategy:
      整数,匹配策略:
      0:精确匹配,路径需与 pattern 完全一致。
      1:前缀匹配,路径以 pattern 开头即可。
      2:正则匹配,使用 pattern 作为正则表达式进行匹配。
6.2.2、流控规则json文件编写(gateway-sentinel-flow-rules.json)

resource目录创建gateway-sentinel-flow-rules.json将流控规则内容填进去。

复制代码
[
  {
    "resource": "user服务API组",
    "resourceMode": 1,
    "count": 1,
    "intervalSec": 1,
    "burst": 0,
    "paramItem": null,
    "controlBehavior": 0,
    "maxQueueingTimeoutMs": 0
  },
  {
    "resource": "kerwin-user",
    "resourceMode": 0,
    "count": 1,
    "intervalSec": 1,
    "burst": 0,
    "paramItem": null,
    "controlBehavior": 0,
    "maxQueueingTimeoutMs": 0
  }
]

字段解释:

  • resource:资源名称,用于标识要进行流控的目标。可以是路由 ID、自定义 API 分组名称等。在网关场景中,资源可以代表一个具体的路由或者一组路由的集合。
  • resourceMode:
    • 0:代表资源模式,对应 SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID,表示基于 Route ID 进行资源匹配。
    • 1:API分组模式,对应 SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME,标识基于API分组进行资源匹配。
  • count:流控阈值,根据 grade 字段的不同,其代表的意义也不同。如果 grade 为 QPS 限流,count 表示每秒允许通过的请求数量;如果 grade 为并发线程数限流,count 表示允许的最大并发线程数。
  • grade:流控阈值类型,取值有两种。1 表示 QPS(每秒查询率)限流,0 表示并发线程数限流。
  • intervalSec:统计时间窗口,单位为秒。表示在多长时间内统计请求数量以判断是否触发流控。
  • controlBehavior:流控效果,即当请求超过阈值时的处理方式
    • 0:快速失败,请求超过阈值时直接拒绝并抛出异常
    • 2:匀速排队,请求会进入队列,按照固定速率处理,避免流量突发
  • burst:仅在 controlBehavior 为 0(快速失败)时有效,表示突发流量容忍值。在短时间内允许超过阈值的额外请求数量,用于应对突发流量场景。
  • maxQueueingTimeoutMs:仅在 controlBehavior 为 2(匀速排队)时有效,表示请求在队列中的最大排队时间,单位为毫秒。超过该时间的请求将被拒绝。
6.2.3、配置文件配置加载API分组和流控规则json文件

在配置文件中添加API分组和流控规则读取数据源配置,这里省略了其它Sentinel配置需要可以看之前配置内容添加。

复制代码
spring:
  cloud:
    sentinel:
      # 设置sentinel为热加载 默认为false
      eager: true
      # API分组&流控规则配置文件配置
      datasource:
        ds1:
          file:
            file: classpath:gateway-sentinel-api-groups.json
            ruleType: gw-api-group # 网关API分组
            dataType: json
        ds2:
          file:
            file: classpath:gateway-sentinel-flow-rules.json
            ruleType: gw-flow # 网关流控规则
            dataType: json

这里就不贴图了,配置完成后启动看看Sentinel控制台是否有初始化这些配置,要记得开启热加载。

七、动态监听Nacos规则配置实时更新流控规则实现(推荐)

通过Nacos配置中心动态拉取加载流控规则和本地配置文件配置是类似的,编写的规则json是一样的,只是需要指定一下加载的数据源为Nacos读取,并且已经实现了动态加载,在Nacos配置中心修改规则会进行实时同步。

7.1、添加sentinel集成nacos包

sentinel读取nacos配置需要添加这个适配包。

复制代码
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
7.2、Nacos配置中心添加API分组规则json(gateway-sentinel-api-groups)

配置的内容和本文6.2.1中一致。

7.3、Nacos配置中心添加流控规则json(gateway-sentinel-flow-rules)

配置的内容和本文6.2.2中一致。

7.4、配置文件配置加载Nacos配置中心API分组和流控规则json
复制代码
spring:
  cloud:
    sentinel:
      # 设置sentinel为热加载 默认为false
      eager: true
      # API分组&流控规则配置文件配置
      datasource:
        ds1:
          nacos:
            server-addr: 172.16.8.169:8848
            data-id: gateway-sentinel-api-groups
            namespace: springcloud-component-example
            group-id: DEFAULT_GROUP
            username: nacos
            password: nacos
            data-type: json
            rule-type: gw-api-group
        ds2:
          nacos:
            server-addr: 172.16.8.169:8848
            data-id: gateway-sentinel-flow-rules
            namespace: springcloud-component-example
            group-id: DEFAULT_GROUP
            username: nacos
            password: nacos
            data-type: json
            rule-type: gw-flow
7.5、测试效果
  • 1、启动网关服务,查看注册API分组和流控规则
  • 2、Nacos配置中心修改流控规则
  • 3、查看sentinel控制台同步情况
相关推荐
蓝眸少年CY19 小时前
(第十二篇)spring cloud之Stream消息驱动
后端·spring·spring cloud
南朝雨1 天前
Spring Boot Admin日志监控坑点:远程配置的logging.file.name为何生效又失效?
java·spring boot·spring cloud·微服务·logback
深入技术了解原理1 天前
eureka-client依赖爆红无法下载
spring cloud·云原生·eureka
ZePingPingZe1 天前
深入理解网络模型之Spring Cloud微服务通信、Socket、HTTP与RPC
网络协议·spring cloud·rpc·dubbo
叫码农就行1 天前
spring cloud 笔记
java·笔记·spring cloud
七夜zippoe1 天前
微服务架构演进实战 从单体到微服务的拆分原则与DDD入门
java·spring cloud·微服务·架构·ddd·绞杀者策略
sunnyday04261 天前
Spring Cloud Alibaba Sentinel 流量控制与熔断降级实战指南
spring boot·sentinel·springcloud
Leo July2 天前
【Java】Spring Cloud 微服务生态全解析与企业级架构实战
java·spring cloud
一条咸鱼_SaltyFish2 天前
WebFlux vs MVC:Gateway集成若依框架的技术选型之争
java·开发语言·微服务·gateway·mvc·开源软件·webflux