CHEN 网关 Sentinel 限流实战:API 分组、规则加载与全量匹配踩坑记
小结 :本文沉淀 CHEN 网关接入 Sentinel 网关流控的完整实战经验。核心内容:① matchStrategy 在 Dashboard UI 上的语义标注(精确/前缀/正则)与客户端适配器真实行为(精确/ANT 模式/正则)严重错位 ,这是"配置看起来对、限流就是不生效"的第一大坑;② gw-api 数据源的 JSON 反序列化由 SCA 自定义反序列化器处理,格式错误时静默失败、每 5 秒重试报错、规则不生效 ;③ 网关自有接口走自转发架构,命中全量分组时限流配额双重消耗。文中给出经过字节码级验证的语义表、生产可用配置与排查 SOP。
姊妹篇:《Spring Cloud Gateway 整合 Sentinel 踩坑记:为什么网关限流抛出的是 ParamFlowException.md》(网关流控规则统一转换为 ParamFlowRule、异常处理返回 429 的部分见该篇,本文不重复展开)。
一、依赖版本(本次踩坑的关键背景)
| 组件 | 版本 | 说明 |
|---|---|---|
| Java | 17 | 编译与运行时 |
| Spring Boot | 3.5.0 | 父 POM |
| Spring Cloud | 2025.0.0 | 对应 Spring Cloud Gateway 4.3.x |
| Spring Cloud Alibaba | 2025.0.0.0 | 统一管理 SCA 全家桶版本 |
| Sentinel 核心/适配器 | 1.8.9 | 显式声明 sentinel-gateway-adapter.version=1.8.9;含 sentinel-spring-cloud-gateway-adapter、sentinel-api-gateway-adapter-common |
| spring-cloud-alibaba-sentinel-gateway | 2025.0.0.0 | SCA 网关自动装配(本文多个坑的来源) |
| spring-cloud-alibaba-sentinel-datasource | 2025.0.0.0 | 规则数据源(Nacos push 模式) |
| Sentinel Dashboard | 1.8.9(企业版) | 部署于 10.xx.xx.161:10000 |
| Nacos | 集群版(k8s 内 nacos-cluster:8848) | 规则配置 push 模式持久化 |
版本组合要点 :SCA 2025.0.0.0 + Sentinel 适配器 1.8.9 是本次所有坑的版本土壤。升级 SCA 或 Sentinel 适配器版本后,本文第 4.1 节的 matchStrategy 语义表必须重新验证。
二、规则生效链路(排障必备心智模型)
Nacos 配置中心
├─ dataId: chen-gateway-gw-api (API 分组定义,rule-type: gw-api-group)
│ → JsonConverter + ApiPredicateItemDeserializer 反序列化
│ → GatewayApiDefinitionManager(内存中的 API 定义 + 匹配器)
│
├─ dataId: chen-gateway-gw-flow-rules (网关流控规则,rule-type: gw-flow)
│ → JsonConverter 反序列化为 GatewayFlowRule
│ → GatewayRuleManager 转换:
│ - 不带 paramItem → FlowRule(普通 QPS 限流)
│ - 带 paramItem → ParamFlowRule(热点参数限流,按 Header/query 参数计数)
│ ⚠️ resourceMode=1(API 分组模式)的规则转换时会校验同名 API 分组是否存在,
│ 分组不存在则规则被丢弃
│
└─ 请求进入 SentinelGatewayFilter(GlobalFilter)
→ pickMatchingApiDefinitions:按 API 分组定义逐个匹配请求路径
→ 命中的分组各建一个 Entry(资源名 = apiName)
→ 未命中任何分组也按 Route ID 建 Entry(资源名 = 路由 ID)
→ ParamFlowRule 检查(超阈值抛 ParamFlowException)
→ GlobalExceptionHandler 捕获 BlockException → HTTP 429 + 业务码 429
链路上任何一环断裂都不会拦截,且大多静默无感知。排障时按"定义加载 → 路径匹配 → 规则转换 → 参数提取"顺序二分定位。
三、生产可用配置(正确答案先行)
3.1 数据源 yml(application-nacos-test.yml)
yaml
spring:
cloud:
sentinel:
eager: true
transport:
dashboard: 10.xx.xx.161:10000
# rule-type 必须写在 nacos 块内(binder 绑定目标无此属性,放外面被忽略 → postRegister NPE)
datasource:
gw-flow:
nacos:
server-addr: ${spring.cloud.nacos.server-addr}
namespace: ${spring.cloud.nacos.config.namespace}
group-id: DEFAULT_GROUP
data-id: chen-gateway-gw-flow-rules
rule-type: gw-flow
gw-api:
nacos:
server-addr: ${spring.cloud.nacos.server-addr}
namespace: ${spring.cloud.nacos.config.namespace}
group-id: DEFAULT_GROUP
data-id: chen-gateway-gw-api
rule-type: gw-api-group
scg:
enabled: true
# webflux 适配器必须关闭,否则与 scg 适配器双重埋点
webflux:
enabled: false
3.2 API 分组定义(chen-gateway-gw-api)
全量请求分组 + 精确路径分组的可用配置:
json
[
{
"apiName": "global-all-requests",
"predicateItems": [
{
"matchStrategy": 1,
"pattern": "/**"
}
]
},
{
"apiName": "blacklist-api",
"predicateItems": [
{
"matchStrategy": 0,
"pattern": "/chen-business/xx/xx/list"
}
]
}
]
3.3 网关流控规则(chen-gateway-gw-flow-rules)
全量按 Authorization 头限流(同 token 5 秒 1 次)+ 路由级兜底:
json
[
{
"burst": 0,
"controlBehavior": 0,
"count": 1.0,
"grade": 1,
"intervalSec": 5,
"maxQueueingTimeoutMs": 500,
"paramItem": {
"fieldName": "Authorization",
"matchStrategy": 0,
"parseStrategy": 2
},
"resource": "global-all-requests",
"resourceMode": 1
},
{
"burst": 0,
"controlBehavior": 0,
"count": 100.0,
"grade": 1,
"intervalSec": 5,
"resource": "chen-business-route",
"resourceMode": 0
}
]
字段说明:resourceMode=0 按 Route ID、=1 按自定义 API 分组;parseStrategy:0=URL 参数、1=Host、2=Header 、3=远程地址;paramItem.matchStrategy=0 表示参数精确匹配。
3.4 matchStrategy 真实语义对照表(字节码级验证,1.8.9)
这是本次踩坑的核心结论。Dashboard UI 的单选框文案与适配器真实行为不一致:
| matchStrategy 值 | Dashboard UI 标注 | 适配器真实行为(1.8.9 SCG 适配器) | 实现类 |
|---|---|---|---|
| 0 | 精确 | 精确匹配(String.equals) |
exactPath |
| 1 | 前缀 | ANT 路径模式匹配(Spring AntPathMatcher,/**、/* 通配符) |
antPath |
| 2 | 正则 | 正则全串匹配(Pattern.matches) |
regexPath |
字节码证据(WebExchangeApiMatcher.fromApiPathPredicate,反编译自 sentinel-spring-cloud-gateway-adapter-1.8.9.jar):
16: invokevirtual getMatchStrategy
20: lookupswitch { 1: 56, 2: 48, default: 64 }
48: RouteMatchers.regexPath(pattern) // matchStrategy=2
56: RouteMatchers.antPath(pattern) // matchStrategy=1
64: RouteMatchers.exactPath(pattern) // 其他(含 0)
由语义错位直接引出的三条铁律:
- 匹配所有请求必须用
matchStrategy=1+pattern="/**"。配pattern="/"(指望"前缀匹配一切")时,AntPathMatcher 判定/不含通配符(isPattern=false),退化为精确匹配路径/,任何真实请求都匹配不上。 - 不要用正则匹配全量 。
matchStrategy=2+"/**"里*是非法正则重复符,模式非法无法匹配。 - 前缀语义要用 ANT 表达 。想匹配
/chen-business/开头的所有路径,写pattern=/chen-business/**(matchStrategy=1),而不是/chen-business/。
四、踩坑记录
4.1 坑一:pattern="/" 配置后 API 分组永不匹配
- 现象 :
global-all-requests分组(matchStrategy=1、pattern=/)在 Nacos 修改后 pod 日志无任何报错,但 Dashboard"请求链路"里只有 Route ID 资源(chen-business-route、gateway-api-route 等),始终不出现global-all-requests,请求从不被拦。 - 对照实验 :同一份数据源里的精确匹配分组(matchStrategy=0)能正常出现在请求链路并限流,证明数据源加载、匹配框架、规则转换全链路是好的------唯独"前缀"分组不工作。
- 根因 :见 3.4 节语义表。matchStrategy=1 走 AntPathMatcher,
/退化为精确匹配。 - 修复 :pattern 改为
/**,即时生效(Nacos 长轮询推送,无需重启 Pod 与 Dashboard)。
4.2 坑二:gw-api JSON 格式错误 → 反序列化失败静默重试
-
现象:gw-api 内容反序列化失败时,pod 日志每 5 秒输出一轮(Nacos 客户端 listener 重试机制):
ERROR c.a.c.s.d.c.SentinelConverter - sentinel rule convert error: Cannot deserialize value of type
java.lang.Stringfrom Object value (tokenJsonToken.START_OBJECT)
(through reference chain: ...ApiDefinition["predicateItems"]->...ApiPathPredicateItem["pattern"])
ERROR c.a.n.client.config.impl.CacheData - [notify-error] dataId=chen-gateway-gw-api ... -
根因 :SCA 2025.0.0.0 的
SentinelGatewayAutoConfiguration$SentinelConverterConfiguration为ApiPredicateItem(抽象类)注册了自定义反序列化器ApiPredicateItemDeserializer,其期望的格式是扁平结构:
json
{"matchStrategy": 1, "pattern": "/"}
若把 pattern 字段写成嵌套对象({"pattern": {"matchStrategy": 1, "pattern": "/"}}),反序列化器把整个 item 转 ApiPathPredicateItem 时 pattern 字段收到 Object 而非 String,直接抛 MismatchedInputException。
- 行为特征 :失败期间 API 分组完全不生效,且不会中断服务、不会在 Dashboard 上有任何提示 ------唯一的信号就是 pod 日志里的这两条 ERROR。所以 gw-api 发布后必看 pod 日志 确认无
sentinel rule convert error。 - 修复:回到 3.2 节标准格式。
4.3 坑三:API 分组加载失败期间,关联流控规则可能被静默丢弃
- 场景:gw-api 反序列化失败(坑二)或分组名写错(坑一)期间,gw-flow 数据源已完成加载。
- 根因 :
GatewayRuleManager把带paramItem的网关规则转换为 ParamFlowRule 时,对resourceMode=1的规则会校验同名 API 分组是否存在,不存在则该条规则被丢弃,无 ERROR 日志。 - 后果:修复 API 分组后,gw-flow 数据源内容未变、不会触发重新加载与转换,被丢弃的规则不会自己回来------"分组修好了,还是不拦"。
- 修复/规程 :修复或新增 API 分组后,必须同步在 Nacos 重新发布一次
chen-gateway-gw-flow-rules(内容不变直接点发布即可),强制触发规则重新转换。
4.4 坑四:网关自有接口自转发 → 全量分组配额双重消耗
- 背景 :网关自身接口(登录
/gateway/xxx/**、渠道管理/gateway/api/xxx/**、渠道权限/gateway/xx/xxion/**)通过路由uri: http://127.0.0.1:${server.port}+ StripPrefix 转发回自身 ,对外统一暴露/gateway前缀。 - 现象 :一次外部调用
/gateway/xx/xx/list,StripPrefix 后的内层请求/xx/xx/list同样命中global-all-requests的/**模式------同一次业务调用在全量分组上消耗 2 次配额。 - 影响评估 :设全量规则 count=N,则网关自有接口实际每窗口只能被调用约 N/2 次;转发类请求(如
/chen-business/**,不自转发)只计 1 次。 - 阈值配置要求:给客户报全量限流阈值时,网关自有接口按"配置值 ÷ 2"折算;或按业务重要性评估是否接受该系数。
4.5 坑五:验证入口与验证姿势
- 验证资源是否匹配 :Dashboard → chen-gateway → **"请求链路"**菜单(该企业版控制台不叫"簇点链路")。先连发几次请求再刷新查看。资源出现的含义:
- Route ID 资源出现 → SCG 适配器在工作(不证明任何规则生效);
- 自定义 API 资源出现 → 分组定义已加载且路径匹配成功;
- 资源出现但"拒绝 QPS"恒为 0 → 匹配 OK,查规则转换(坑三)与 paramItem 参数提取。
- "实时监控"菜单只展示有 metric 的资源,不能作为规则加载依据。
- 测试姿势:count=1/intervalSec=5 的规则,手动点两次的间隔很容易超过 5 秒窗口造成"没拦"的误判------用 Apifox 循环连发验证。
- 429 判定:被限流的请求由 GlobalExceptionHandler 统一返回 HTTP 429 + 业务码 429(详见姊妹篇)。
五、排查 SOP(按顺序二分)
text
gw-api 改配置 → 发布
├─ ① 看 pod 日志:grep "sentinel rule convert error"
│ 有 → JSON 格式错(回到坑二),修格式,重新走 ①
│ 无 → 进入 ②
├─ ② 重新发布 gw-flow(坑三规程)
└─ ③ 连发请求 + Dashboard"请求链路"刷新
├─ 自定义 API 资源没出现 → 匹配失败:查 pattern/matchStrategy(坑一)
├─ 资源出现但不 429 → 规则未转换(坑三,确认 ② 做了)
│ 或 paramItem 提取问题(确认请求头确实带 Authorization)
└─ 429 了 → 链路全通
六、运维约定
- API 分组(chen-gateway-gw-api)只在 Nacos 控制台维护,禁止在 Dashboard UI 编辑------Dashboard 的"编辑自定义 API"对话框按"精确/前缀/正则"文案生成 matchStrategy,与适配器真实语义错位(3.4 节),且编辑保存会推送覆盖 Nacos 里的正确配置,直接复现坑一。
- 流控规则(chen-gateway-gw-flow-rules)同样建议在 Nacos 维护,Dashboard 仅用于监控(请求链路/实时监控)。
- 新增/修复 API 分组后,必须重新发布 gw-flow(坑三)。
- Nacos 规则变更为长轮询实时生效,不需要重启网关 Pod、不需要重启 Dashboard。
- 全量分组阈值评估必须计入自转发双重消耗系数(坑四)。
- 升级 SCA / Sentinel 适配器版本时,重新验证 3.4 节语义表与 4.2 节 JSON 格式。