本文进行 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 的处理模式。一共有response
、redirect
、空三种选择。
当 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
,可以看到网关已经成功注册上。如下图所示:
② 点击「流控规则」菜单,我们来给路由
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 分组的单独流控规则,允许每个 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 限流。
返回限流提示:
{
"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 的字段解释如下:
-
apiName
:分组名。 -
predicateItems
:匹配规则(ApiPathPredicateItem)数组。
具体的示例,可以看看 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
}
]
}
]
}
]
控制台查看:
matchStrategy的值0、1、2含义除了查看文档,还可以通过sentinel控制台菜单对应。
底线
本文源代码使用 Apache License 2.0开源许可协议,这里是本文源码Gitee地址,可通过命令git clone+地址
下载代码到本地,也可直接点击链接通过浏览器方式查看源代码。