Spring Cloud Gateway(三、Sentinel 实现服务流控、容错)

本文进行 Gateway 和 Sentinel 的整合,使用 Sentinel 进行 Gateway 的流量保护。Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级流量控制产品,主要以流量为切入点,从流量控制熔断降级系统负载保护等多个维度来帮助用户保护服务的稳定性。

Sentinel 提供了 sentinel-spring-cloud-gateway-adapter 子项目,已经对 Gateway 进行适配,所以我们只要引入它,基本就完成了 Gateway 和 Sentinel 的整合。

下面,我们来搭建 Gateway 基于 Sentinel 实现服务容错的使用示例。

一、引入依赖

    <dependencies>
        <!-- 引入 Spring Cloud Gateway 相关依赖,使用它作为网关,并实现对其的自动配置 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖,使用 Sentinel 提供服务保障,并实现对其的自动配置 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
    </dependencies>

二、配置文件

修改 [application.yaml]配置文件,增加 Sentinel 的配置项。完整配置如下:

server:
  port: 8888

spring:
  application:
    name: gateway-application-sentinel

  cloud:
    # Spring Cloud Gateway 配置项,对应 GatewayProperties 类
    gateway:
      # 路由配置项,对应 RouteDefinition 数组
      routes:
        - id: guoxiuzhi # 路由的编号
          uri: https://www.126.com # 路由的目标地址
          predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
            - Path=/*
          filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组
            - StripPrefix=1

    sentinel:
      eager: true # 是否饥饿加载。默认为 false 关闭
      transport:
        dashboard: localhost:8080 # Sentinel 控制台启动在 8080 端口。

      # Sentinel 对 Spring Cloud Gateway 的专属配置项,对应 SentinelGatewayProperties 类
      scg:
        order: -2147483648 # 过滤器顺序,默认为 -2147483648 最高优先级
        fallback:
          mode: # fallback 模式,目前有三种:response、redirect、空(我们走自定义fallback类内容)

          # 对应 response 模式
          response-status: 429 # 响应状态码,默认为 429
          response-body: This request is blocked by erbadagang's sentinel. # 响应内容,默认为空
          content-type: application/json # 内容类型,默认为 application/json

          # 对应 redirect 模式
          redirect: http://www.baidu.com

① 为了测试方便,我们修改 spring.cloud.gateway.routes 配置项,所有请求都转发到www.126.com

spring.cloud.sentinel 配置项,是 Spring Cloud Sentinel 的配置项,可以看看Sentinel 入门文章。

友情提示:本机搭建的 Sentinel 控制台启动在 8080 端口。

spring.cloud.sentinel.scg 配置项,是 Sentinel 对 Spring Cloud Gateway 的专属配置项,对应 SentinelGatewayProperties类。

  • order:过滤器顺序,默认为 -2147483648 最高优先级。
  • fallback:Sentinel fallback 的处理模式。一共有 responseredirect三种选择。

fallback.mode 选择时,使用 Sentinel 定义的 BlockRequestHandler 处理 fallback 的情况。默认情况下,使用 BlockRequestHandler 默认实现类 DefaultBlockRequestHandler

当然,我们也可以自己实现 BlockRequestHandler 接口,实现对 fallback 的自定义处理逻辑。例如说本示例,我们就创建了 [CustomBlockRequestHandler]类,进行下演示,代码如下:

package com.erbadagang.springcloud.gateway.sentinel.handler;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @description 自定义的限流返回信息。
 * @ClassName: CustomBlockRequestHandler
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/7/31 11:21
 * @Copyright:
 */
@Component
public class CustomBlockRequestHandler implements BlockRequestHandler {

    private static final String DEFAULT_BLOCK_MSG_PREFIX = "This request is blocked by guoxiuzhi's sentinel.";

    @Override
    public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable ex) {
        String bodyJson = "{\n" +
                "  \"code\": 429,\n" +
                "  \"data\": \"%s The detail exception class: %s \"\n" +
                "}";

        return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS) // 状态码
                .contentType(MediaType.TEXT_PLAIN) // 内容类型为 text/plain 纯文本
                .bodyValue(String.format(bodyJson, DEFAULT_BLOCK_MSG_PREFIX, ex.getClass().getSimpleName())); // 错误提示
    }

}

实际项目中,可以根据自己的返回信息的格式进行定制。

三、GatewayApplication

修改 [GatewayApplication](类的代码,声明这是一个 Spring Cloud Gateway 应用。代码如下:

package com.erbadagang.springcloud.gateway.sentinel;

import com.alibaba.cloud.sentinel.gateway.ConfigConstants;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @description 网关系统启动类,注意代码中设置应用类型为 Spring Cloud Gateway。
 * @ClassName: GatewayApplication
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/7/31 11:09
 * @Copyright:
 */
@SpringBootApplication
public class GatewayApplication {

    public static void main(String[] args) {
        // 【重点】设置应用类型为 Spring Cloud Gateway
        System.setProperty(SentinelConfig.APP_TYPE, ConfigConstants.APP_TYPE_SCG_GATEWAY);

        SpringApplication.run(GatewayApplication.class, args);
    }

}

四、测试

① 执行 GatewayApplication 启动网关。
访问 Sentinel 控制台,http://localhost:8080/,用户名和密码都是:sentinel,可以看到网关已经成功注册上。如下图所示:

Sentinel 控制台

② 点击「流控规则」菜单,我们来给路由 guoxiuzhi 创建一个网关流控规则,如下图所示:
设定流控规则

  • 创建了针对路由 guoxiuzhi统一流控规则,允许每个 URL 的 QPS 上限为 2。

使用浏览器,快速访问http://localhost:8888/地址 3 次,会发现被 Sentinel 限流,返回 :

{
  "code": 429,
  "data": "This request is blocked by guoxiuzhi's sentinel. The detail exception class: ParamFlowException "
}

该文字提示,就是我们自定义的CustomBlockRequestHandler提供的。

每次重启应用,sentinel的配置由于在内存中会丢失。解决方案见Sentinel规则持久化文章

③ 下面,我们来给 /erbadagang/** 路径,配置单独流控规则。

点击「API 管理」菜单,我们先创建一个包含 /erbadagang/**API 分组,如下图所示:

自定义API分组

当然该分组可以配置多个匹配模式和匹配串来统一管理一类的API。
点击「流控规则」菜单,我们再给 API 分组创建一个网关流控规则,如下图所示:
API分组的流控规则

  • 创建了针对该 API 分组的单独流控规则,允许每个 URL 的 QPS 上限为 1。

追加erbadagang的route配置

        - id: erbadagang # 路由的编号,在sentinel控制台会用到。
          uri: http://www.jianshu.com/ # 路由的目标地址
          predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
            - Path=/erbadagang/*
          filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组
            - StripPrefix=1

使用postman,模拟5次快速访问 http://localhost:8888/erbadagang/u/ea0462d5074c 地址 ,会发现被 Sentinel 限流。

新建postman runner

Runner配置

运行结果

返回限流提示:

{
    "code": 429,
    "data": "This request is blocked by guoxiuzhi's sentinel. The detail exception class: ParamFlowException "
}

注意,虽然我们给 /erbadagang/** 配置了单独的流控规则,但是前面通过路由配置的统一的流控规则也是生效的,也会作用到 /erbadagang/** 上,即叠加的效果。

五、文件方式配置限流规则

首先yml文件新增配置来识别自定义的json数据源:

server:
  port: 8888

spring:
  application:
    name: gateway-application-sentinel

  cloud:
    # Spring Cloud Gateway 配置项,对应 GatewayProperties 类
    gateway:
      # 路由配置项,对应 RouteDefinition 数组
      routes:
        - id: guoxiuzhi # 路由的编号,在sentinel控制台会用到。
          uri: https://www.126.com # 路由的目标地址
          predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
            - Path=/*
          filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组
            - StripPrefix=1

        - id: erbadagang # 路由的编号,在sentinel控制台会用到。
          uri: http://www.jianshu.com/ # 路由的目标地址
          predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
            - Path=/erbadagang/*
          filters: # 过滤器,对请求进行拦截,实现自定义的功能,对应 FilterDefinition 数组
            - StripPrefix=1

    sentinel:
      eager: true # 是否饥饿加载。默认为 false 关闭
      transport:
        dashboard: localhost:8080 # Sentinel 控制台启动在 8080 端口。
      # 数据源的配置项,实现限流规则和API分组的文档化配置。
      datasource:
        ds1.file:
          file: "classpath: sentinel-gw-flow.json"
          ruleType: gw-flow
        ds2.file:
          file: "classpath: sentinel-gw-api-group.json"
          ruleType: gw-api-group

      # Sentinel 对 Spring Cloud Gateway 的专属配置项,对应 SentinelGatewayProperties 类
      scg:
        order: -2147483648 # 过滤器顺序,默认为 -2147483648 最高优先级
        fallback:
          mode: # fallback 模式,目前有三种:response、redirect、空(可以实现对 fallback 的自定义处理逻辑)

          # 对应 response 模式的fallback配置
          response-status: 429 # 响应状态码,默认为 429
          response-body: This request is blocked by erbadagang's sentinel. # 响应内容,默认为空
          content-type: application/json # 内容类型,默认为 application/json

          # 对应 redirect 模式的配置fallback配置
          redirect: http://www.baidu.com

2个json文件在resources目录下:
sentinel-gw-flow.json

[
  {
    "resource": "erbadagang",
    "count": 3
  },
  {
    "resource": "erbadagang_customized_api",
    "count": 1
  }
]

sentinel-gw-api-group.json

[
  {
    "apiName": "erbadagang_customized_api",
    "predicateItems": [
      {
        "pattern": "/erbadagang/**",
        "matchStrategy": 1
      },
      {
        "items": [
          {
            "pattern": "/Dubbo/good-collection/",
            "matchStrategy": 0
          },
          {
            "pattern": "/SkyWalking/**",
            "matchStrategy": 1
          }
        ]
      }
    ]
  }
]

sentinel-spring-cloud-gateway-adapter 项目增加了网关限流规则GatewayFlowRule),针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。

GatewayFlowRule 的字段解释如下:

  • resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
  • resourceMode:规则是针对 API Gateway 的 route 还是用户在 Sentinel 中定义的 API 分组,默认是 route。
  • grade:限流指标维度,同限流规则grade 字段。
  • count:限流阈值
  • intervalSec:统计时间窗口,单位是秒,默认是 1 秒。
  • controlBehavior:流量整形的控制效果,同限流规则controlBehavior 字段,目前支持快速失败和匀速排队两种模式,默认是快速失败。
  • burst:应对突发请求时额外允许的请求数目。
  • maxQueueingTimeoutMs:匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。
  • paramItem:参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。其中的字段:
    • parseStrategy:从请求中提取参数的策略,目前支持提取来源 IP、Host、任意 Header 和任意 URL 参数四种策略。
    • fieldName:若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。
    • pattern:参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则统计该请求属性的所有值。
    • matchStrategy:参数值的匹配策略,目前支持精确匹配、子串匹配和正则匹配三种策略。

具体的示例,可以看看 sentinel-gw-flow.json 配置文件,内容如下:

[
  {
    "resource": "erbadagang",
    "count": 3
  },
  {
    "resource": "erbadagang_customized_api",
    "count": 1
  }
]

启动gateway服务后,在sentinel控制台查看配置:
文件方式配置流控规则

六、文件方式配置API分组

sentinel-spring-cloud-gateway-adapter 项目增加了API 定义分组ApiDefinition),用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api,请求 path 模式为 /foo/**/baz/**的都归到 my_api 这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。

ApiDefinition 的字段解释如下:

具体的示例,可以看看 sentinel-gw-api-group.json 配置文件,内容如下:

[
  {
    "apiName": "erbadagang_customized_api",
    "predicateItems": [
      {
        "pattern": "/erbadagang/**",
        "matchStrategy": 1
      },
      {
        "items": [
          {
            "pattern": "/Dubbo/good-collection/",
            "matchStrategy": 0
          },
          {
            "pattern": "/SkyWalking/**",
            "matchStrategy": 1
          }
        ]
      }
    ]
  }
]

控制台查看:


配置文件方式的API分组

matchStrategy的值0、1、2含义除了查看文档,还可以通过sentinel控制台菜单对应。

底线


本文源代码使用 Apache License 2.0开源许可协议,这里是本文源码Gitee地址,可通过命令git clone+地址下载代码到本地,也可直接点击链接通过浏览器方式查看源代码。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。