Bootstrap

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:
    字符串,代表要进行流量控制的资源名称,这里是 “kerwin-user”,与之前 Java 代码中的资源名称一致。
  • resourceMode:
    整数,代表资源模式,这里设置为 0,对应 SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID,表示基于路由 ID 进行资源匹配。
  • count:
    整数,代表 QPS 阈值,这里设置为 1,表示每秒允许通过的请求数量为 1 个。
  • intervalSec:
    整数,代表时间间隔,这里设置为 1 秒,用于计算 QPS 的时间范围。
  • burst:
    整数,代表突发请求的数量,这里设置为 0,表示不允许突发请求超过 QPS 阈值。
  • paramItem:
    可为对象或 null,可用于根据请求参数进行限流,这里设置为 null,表示不基于请求参数进行限流。
  • controlBehavior:
    整数,代表控制行为,这里设置为 0,表示直接拒绝策略,当请求超过阈值时,直接拒绝新的请求。
  • maxQueueingTimeoutMs:
    整数,代表最大排队超时时间(毫秒),这里设置为 0,表示不使用排队策略。
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控制台同步情况
    在这里插入图片描述
;