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+地址下载代码到本地,也可直接点击链接通过浏览器方式查看源代码。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354