掘金 后端 ( ) • 2024-04-23 16:01

网关流控定义

Sentinel支持对Spring Cloud Gateway、Zuul等主流的API Gataway 进行限流,作用在网关的流控称之为网关流控,其实现原理请点击网关限流进入官方Wiki查看。

这里只把原理图从官方文档摘出来,需多关注图中提到的模块名和几个类名,因为都是核心级别的存在。

image.png

规则类型gw-flowgw-api-group为网关流控规则,具体类型请查看规则类型枚举RuleType

/**
* flow 流控规则
*/
FLOW("flow", FlowRule.class),
/**
* degrade 降级规则
*/
DEGRADE("degrade", DegradeRule.class),
/**
* param flow 热点规则
*/
PARAM_FLOW("param-flow", ParamFlowRule.class),
/**
* system 系统规则
*/
SYSTEM("system", SystemRule.class),
/**
* authority 授权规则
*/
AUTHORITY("authority", AuthorityRule.class),
/**
* gateway flow 网关限流规则
*/
GW_FLOW("gw-flow","com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"),
/**
* api 用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合
*/
GW_API_GROUP("gw-api-group","com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");

image.png

网关流控规则

Field 说明 默认值 resource 资源名称,网关route或自定义API分组名称(注:网关route这里的值不是route.id,可调试 resourceMode 限流资源类型,网关route【0】或自定义API分组【1】(详细查看GatewayFlowRuleSentinelGatewayConstants) 网关route grade 限流阈值类型,QPS【1】或线程数【0】 QPS count 限流阈值,QPS阈值或线程数值 intervalSec 统计时间间隔,单位秒 1秒 controlBehavior 流控效果,目前支持快速失败【0】和匀速排队【1】 快速失败 burst 应对突发请求时额外允许的请求数目 maxQueueingTimeoutMs 匀速排队模式下的最长排队时间,单位毫秒,仅在匀速排队模式下生效 paramItem 参数属性配置,parseStrategy:提取参数策略(0:Clien IP,1:Remote HOST,2:Header,3:请求参数,4:Cookie);fieldName:若提取策略是Header模式或者URL参数模式,则需要指定header名称或URL参数名称;pattern:参数值的匹配模式;matchStrategy:参数值的匹配策略,支持精确匹配,子串匹配和正则匹配。

网关流控客户端标识

网关流控和普通流控有很多区别,其中网关流控类型是gw-flow,普通流控类型是flow

怎么标识流控是网关类型呢?

很多博客文章都没有着重此点,因为前阵子纠结于网关流控的面板和普通流控的面板不一致而去搜相关的资料,最后还是在Sentinel官方文档中找到此开关,就是需要在gateway-sentinel网关应用添加JVM启动参数。

# 注:通过 Spring Cloud Alibaba Sentinel 自动接入的 API Gateway 整合则无需此参数
-Dcsp.sentinel.app.type=1

具体如下图: image.png

或者在启动类中main方法中添加如下代码:

// 添加此代码,在Sentinel控制台中做判断使用
System.setProperty("csp.sentinel.app.type", "1");

集成

  1. 新建gateway-sentinel模块,并导入如下依赖
<!--nacos注册中心-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!--spring cloud gateway-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<!--spring cloud gateway整合sentinel的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

<!--sentinel的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<!-- Sentinel规则持久化至Nacos配置 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.8.7</version>
</dependency>
  1. 网关配置,在application.yml添加nacosSentinel的配置,同时配置路由转发
server:
  port: 10015

spring:
  application:
    name: gateway-sentinel
  cloud:
    # 整合sentinel,配置sentinel控制台的地址
    sentinel:
      web-context-unify: false
      datasource:
        gw-api-group:
          nacos:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            username: ${spring.cloud.nacos.discovery.username}
            password: ${spring.cloud.nacos.discovery.password}
            namespace: ${spring.cloud.nacos.discovery.namespace}
            group-id: SENTINEL_GROUP
            data-id: ${spring.application.name}-gateway-api-rules
            # 规则类型:flow、degrade、param-flow、system、authority、gw-flow、gw-api-group
            rule-type: gw-api-group
        gw-flow:
          nacos:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            username: ${spring.cloud.nacos.discovery.username}
            password: ${spring.cloud.nacos.discovery.password}
            namespace: ${spring.cloud.nacos.discovery.namespace}
            group-id: SENTINEL_GROUP
            data-id: ${spring.application.name}-gateway-flow-rules
            # 规则类型:flow、degrade、param-flow、system、authority、gw-flow
            rule-type: gw-flow
        # 熔断降级
        degrade:
          nacos:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            username: ${spring.cloud.nacos.discovery.username}
            password: ${spring.cloud.nacos.discovery.password}
            namespace: ${spring.cloud.nacos.discovery.namespace}
            group-id: SENTINEL_GROUP
            data-id: ${spring.application.name}-degrade-rules
            rule-type: degrade
        # 系统规则
        system:
          nacos:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            username: ${spring.cloud.nacos.discovery.username}
            password: ${spring.cloud.nacos.discovery.password}
            namespace: ${spring.cloud.nacos.discovery.namespace}
            group-id: SENTINEL_GROUP
            data-id: ${spring.application.name}-system-rules
            rule-type: system
      transport:
        ## 指定控制台的地址,默认端口8080
        dashboard: 192.168.0.102:6005
        client-ip: 192.168.0.104
        port: 8719
      eager: true
    nacos:
      # 注册中心配置
      discovery:
        # nacos的服务地址,nacos-server中IP地址:端口号
        server-addr: 192.168.0.102:8848
        username: nacos
        password: nacos
        namespace:
    gateway:
      # 路由
      routes:
        # id只要唯一即可,名称任意
        - id: nacos-sentinel
          uri: http://localhost:9004
          ## 配置断言
          predicates:
            - Path=/sentinel/**
management:
  endpoints:
    web:
      exposure:
        ## yml文件中存在特殊字符,必须用单引号包含,否则启动报错
        include: '*'

这里解释下配置中的datasource,因为在Sentinel添加流控规则之后,如果重启服务,之前配置的规则就会消失,所>以这里需要持久化Sentinel配置,从上面的配置可以看出选择的是Nacos。

网关流控测试

  1. 启动gateway-sentinel模块

  2. 启动nacos-sentinel模块

  3. 打开sentinel控制台,给nacos-sentinelapi添加流控规则,设置阈值为5,即1秒最多发5次请求 image.png

  4. 打开jmeter,添加Thread Group,设置1秒10次请求 image.png

    image.png

  5. 添加HTTP请求 image.png

    image.png

  6. 添加察看结果树,因为要看请求的响应,所以这里添加察看结果树 image.png

  7. 启动线程组,每秒15次认证请求

    需要注意的是,如果测试计划有多个线程组,需禁用除了测试之外的其他线程组。

  8. 点击查看结果树查看请求的情况 image.png

  9. 进入Sentinel控制台,查看实时监控 image.png

自定义网关流控异常

上面Sentinel限流的默认异常响应如下

{"code":429,"message":"Blocked by Sentinel: ParamFlowException"}

假如想自定义网关流控异常响应,该如何实现呢?

可以通过在GatewayCallbackManager上通过setBlockHandler方法注册回调实现,当请求被限流后,实现自定义的异常响应。

image.png 自定义异常代码:

package com.example.config;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.example.enums.CodeEnum;
import com.example.utils.ResponseResult;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;

import javax.annotation.PostConstruct;

@Configuration
public class SentinelConfig {

    @PostConstruct
    private void initBlockHandler() {
        BlockRequestHandler blockRequestHandler = (exchange, t) ->
                ServerResponse.status(CodeEnum.FLOW_LIMIT.getCode())
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(ResponseResult.builder().error(CodeEnum.FLOW_LIMIT).build()));
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

JMeter中查看被限流的响应,可以看到已按照自定义的响应异常返回 image.png

Java开发手册上的关于系统限流的错误码默认为B0210