Gateway 网关流控与限流架构指南
1. 整体架构概述
项目中搭建一套完整的基于 Sentinel + Nacos 动态规则 的 Gateway 流控体系,主要作用是保护微服务,防止瞬时高并发流量压垮系统。网关层的流控生效在所有请求进入具体微服务之前。
流控架构由以下三个核心部分组成:
- 拦截生效层 :依靠
spring-cloud-alibaba-sentinel-gateway,自动将所有的 Gateway 路由(Route)注册为 Sentinel 保护的资源。 - 配置持久层:依靠 Nacos 配置中心,持久化存储各种流控规则(如限流 QPS、排队等待策略等)。
- 动态同步层 :依靠
sentinel-datasource-nacos,Gateway 启动时会连接 Nacos,并监听对应的 Data ID。一旦在 Nacos 修改流控规则,Gateway 不需重启即可实时生效。
2. 核心基础设施
2.1 引入的核心依赖
在 Gateway 模块的 pom.xml 中,已引入完整的 Sentinel 环境支持:
spring-cloud-starter-alibaba-sentinel:Sentinel 核心框架。spring-cloud-alibaba-sentinel-gateway:专门适配 Spring Cloud Gateway 的模块。sentinel-datasource-nacos:提供从 Nacos 动态拉取规则的功能。
2.2 核心配置说明
在 application.properties 中,已经做好了针对各环境(DEV、UAT、PROD)的无缝动态规则配置:
sql
# 1. 禁用原生的基于 Servlet Filter 的过滤,因为 Gateway 基于 WebFlux
spring.cloud.sentinel.filter.enabled=false
# 2. 网关流控触发降级时的默认响应模式:返回 Response
spring.cloud.sentinel.scg.fallback.mode=response
# 3. Sentinel Dashboard 控制台通信端口配置
spring.cloud.sentinel.transport.port=${SENTINEL_TRANSPORT_PORT:8719}
spring.cloud.sentinel.transport.dashboard=${SENTINEL_TRANSPORT_DASHBOARD:localhost:8780}
# 启动时即刻初始化与控制台的连接,而不等待第一个流量到来
spring.cloud.sentinel.eager=${SENTINEL_EAGER:true}
# 4. ⭐ 核心配置:Nacos 动态数据源设置
spring.cloud.sentinel.datasource.ds1.nacos.server-addr=${NACOS_SERVER_ADDR}
# Rule 文件在 Nacos 中的 Data ID,例如 gateway-gw-flow-sentinel
spring.cloud.sentinel.datasource.ds1.nacos.data-id=${spring.application.name}-gw-flow-sentinel
# Rule 文件的 Group ID (例如 DEV_GROUP / PROD_GROUP) 在各环境特有的 properties 中指定
spring.cloud.sentinel.datasource.ds1.nacos.data-type=json
# 指定拉取的数据是用于网关流控配置 (GatewayFlowRule)
spring.cloud.sentinel.datasource.ds1.nacos.rule-type=gw-flow
3. 如何配置流控规则(操作指引)
网关流控是完全热更新的,不需要修改或发版任何代码 ,只需在 Nacos 配置中心 进行操作:
- 登录 Nacos 管理控制台。
- 进入【配置管理】 -> 【配置列表】。
- 选择当前对应环境的 Namespace。
- 创建配置:
- Data ID :
${项目名}-gw-flow-sentinel(例如gateway-gw-flow-sentinel)。 - Group :DEV环境选
DEV_GROUP,PROD选PROD_GROUP(依据各环境application-{env}.properties spring.cloud.sentinel.datasource.ds1.nacos.group-id定义)。 - 配置格式 :选择
JSON。
- Data ID :
- 在配置内容中填入流控规则数组(详见下一小节)。
- 点击发布,Gateway 服务会立即热加载该规则并生效。
json
[
{
"resource": "user-center",
"resourceMode": 0,
"grade": 1,
"count": 100,
"intervalSec": 1,
"controlBehavior": 0,
"burst": 0,
"maxQueueingTimeoutMs": 500,
"paramItem": null
}
]
核心字段说明
| 字段 | 含义 | 值说明 |
|---|---|---|
| resource | 匹配的资源名 | 对应 routes[].id,例如 user-center、product |
| resourceMode | 资源模式 | 0=Route ID(按路由), 1=API Group(按自定义 API 分组) |
| grade | 限流类型 | 1=QPS, 0=线程数 |
| count | 阈值 | QPS=100 表示每秒最多100个请求 |
| intervalSec | 统计时间窗口 | 默认 1(秒) |
| controlBehavior | 限流策略 | 0=快速失败, 1=匀速排队, 2=预热 |
| burst | 额外允许的突发量 | 配合快速失败使用 |
| maxQueueingTimeoutMs | 排队最大等待时间 | 配合匀速排队使用 |
| paramItem | 参数级热点限流 | 见下方高级用法 |
4. 实战流控规则配置(JSON 格式)
Sentinel Gateway 流控规则采用 JSON 数组格式,一个 JSON 对象代表一条针对网关资源的规则。
场景一:按 Route ID(路由)限流
这是最常见的情况,如限制进入 user-center 服务的请求不超过 200 QPS。
json
[
{
"resource": "user-center", // 对应 application.properties 中 gateway.routes[0].id
"resourceMode": 0, // 0 代表 Route ID 模式,1 代表自定义 API 分组模式
"grade": 1, // 1 代表根据 QPS 限流,0 代表根据线程数限流
"count": 200, // QPS 阈值为 200
"intervalSec": 1, // 统计时间窗口 1 秒
"controlBehavior": 0 // 控制行为:0 表示快速失败(直接返回降级响应)
}
]
场景二:按自定义 API 路径限流
仅限制某个特定的微服务接口,比如登录接口,需先在 Java 代码(如 GatewayConfig)中建立 API 分组(GatewayApiDefinitionManager.loadApiDefinitions(...)),再在 Nacos 中映射该分组名称:
json
[
{
"resource": "login-api", // 对应 Java 代码里面定义的自定义 API 分组名
"resourceMode": 1, // 使用自定义 API 模式
"grade": 1,
"count": 50,
"intervalSec": 1,
"controlBehavior": 0
}
]
java
@Configuration
public class SentinelGatewayConfig {
@PostConstruct
public void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
// 定义一个 API 分组:login-api
ApiDefinition loginApi = new ApiDefinition("login-api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem()
.setPattern("/api/user-center/main/login") // 精确匹配
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT));
add(new ApiPathPredicateItem()
.setPattern("/api/user-center/main/login**") // 前缀匹配
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(loginApi);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
}
场景三:匀速排队策略(削峰填谷)
如果后端服务处理能力死板,不想快速拒绝请求,可以开启匀速排队策略:
json
[
{
"resource": "trade-center",
"resourceMode": 0,
"grade": 1,
"count": 50, // 每秒精确通过 50 个请求(相当于每 20ms 执行一次请求)
"intervalSec": 1,
"controlBehavior": 1, // 控制行为:1 代表匀速排队
"maxQueueingTimeoutMs": 2000 // 如果在队列中等待超过 2000ms 还没轮到,才进行拒绝抛出异常
}
]
场景四:基于参数(热点 IP/Header)限流
如果想要限制同一个 IP 对某个服务的疯狂调用:
json
[
{
"resource": "user-center",
"resourceMode": 0,
"grade": 1,
"count": 20, // 每一个 IP 在 1 秒内针对 user-center 只能发 20 个请求
"intervalSec": 1,
"controlBehavior": 0,
"paramItem": {
"parseStrategy": 0 // 0 代表按照客户端真实 IP 进行统计 (依赖于 Gateway 的 RemoteAddressResolver 解析)
}
}
]
parseStrategy 其他可用值包括:2(按 Header 限流)、3(按 URL 参数限流)、4(按 Cookie 限流)。当值为 2/3/4 时,需要配合属性 fieldName 指定具体的 Header、参数名称。
5. 自定义触发限流后的响应
默认响应的是 Sentinel 原生自带的如 Blocked by Sentinel (flow limiting) 文字。推荐后续在 Gateway 项目下增加一个专门的 BlockRequestHandler 来统一个团队风格的 JSON 输出报文。
例如,实现 BlockRequestHandler 并交由 Spring 托管,当触发限流后返回:
json
{
"code": 429,
"msg": "系统繁忙,请求被限流,请稍后重试",
"data": null
}
或者用代码方式(更灵活):
java
@Configuration
public class SentinelFallbackConfig {
@PostConstruct
public void initBlockHandler() {
GatewayCallbackManager.setBlockHandler((exchange, throwable) -> {
Map<String, Object> result = new HashMap<>();
result.put("code", 429);
result.put("message", "系统繁忙,请稍后重试");
result.put("timestamp", System.currentTimeMillis());
return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(result));
});
}
}