RuoYi-Cloud 接入 Sentinel 的 3 种限流方式
场景:
-
服务:
ruoyi-robot
(对外接口统一在/external/gs/**
) -
网关:
ruoyi-gateway
(转发到ruoyi-robot
) -
注册/配置:Nacos
-
流控:Sentinel 1.8.x + 控制台 Dashboard(我用 8718 端口)
-
目标:既能“一把梭”限整个对外能力(按 URL 前缀),也能给个别 API 做业务化兜底提示
总览:三种限流姿势放在一起怎么选?
方案 | 生效层级 | 是否改 robot 代码 | 规则是否持久化 | 命中后的典型返回 | 适用场景 |
---|---|---|---|---|---|
A. 服务内方法级 @SentinelResource | 服务 | 要(按方法) | 可持久化,也可不持久化 | 200 + 业务 JSON(我自定义429) | 精细化/业务化兜底 |
B. 网关控制台临时规则 | 网关 | 否 | ❌ | HTTP 500(默认) | 演示/排障,重启丢失 |
C. 网关 API 分组 + Nacos 规则(推荐) | 网关 | 否 | ✅(Nacos) | HTTP 500 | 给整组 URL 做统一限流 |
官方参考文档:服务网关 | RuoYi
A. 服务内方法级限流(@SentinelResource
)
1)服务接入 Sentinel 控制台
ruoyi-robot-dev.yml
(节选)
spring:cloud:sentinel:eager: truetransport:dashboard: 127.0.0.1:8718
目的:ruoyi-robot
也把自身资源(URL、注解资源)上报到控制台,便于在服务内做细粒度流控/降级。
2)对具体方法加注解与兜底方法
以
/external/gs/robots
为例
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;@SentinelResource(value = "listRobots",blockHandler = "listRobotsBlockHandler",fallback = "listRobotsFallback")
@GetMapping("/robots")
public AjaxResult listRobots(@RequestParam(value = "robotSn", required = false) String robotSn) {// ...业务逻辑...return AjaxResult.success(robots);
}/** 限流/熔断被触发时走这里(签名要匹配,最后一个参数必须是 BlockException) */
public AjaxResult listRobotsBlockHandler(String username, BlockException ex) {return AjaxResult.error("请求超过最大数,请稍候再试"); // 你现在返回的是 code=429 的 JSON
}
注意:
-
blockHandler
/fallback
方法的返回类型要与原方法一致,参数也要能匹配(blockHandler
最后必须有BlockException
)。 -
兜底方法可以写到同类或独立类(独立类需
public static
)。
3)命中时的返回差异
-
这种方式是在服务内处理,你返回的是 业务 JSON(比如
{"code":429,"msg":"Too Many Requests"}
),HTTP 状态多半还是 200。 -
如果你希望连 HTTP 状态也用 429,可以在
blockHandler
里抛出带 429 的异常并用全局异常处理器转成响应;或统一让网关(方案 A/B)兜底。
4)如何“看见”和“控住”这个资源
-
只要接口被调用一次,控制台就会在 ruoyi-robot 的
sentinel_spring_web_context
和你自定义的"listRobots"
资源下出现统计。 -
在 ruoyi-robot 应用上新增一条针对
listRobots
的流控规则(比如 QPS=1),即可触发blockHandler
。
B. 网关控制台临时规则(不持久化)
-
做法:直接在 Sentinel 控制台给 ruoyi-gateway 新增流控规则(可选按 Route,也可选 API 分组)。
-
特点:重启规则就没了,适合验想法/演示。
-
行为:和 A 一样,返回 500,只是数据不落 Nacos。
C. 网关「API 分组」限流
1)Nacos 保存网关规则(持久化)
-
API 分组定义(
ruoyi-gateway-gw-api-defs
) -
[{"apiName": "external-gs-api","predicateItems": [{ "pattern": "/external/gs/**", "matchStrategy": 1 }]} ]
分组限流规则(
ruoyi-gateway-gw-flow
) -
[{"resource": "external-gs-api","resourceMode": 1, // 1=API分组, 0=Route"grade": 1, // 1=QPS"count": 1, // 阈值 1 QPS"intervalSec": 1,"controlBehavior": 0,// 直接拒绝"burst": 0} ]
2)网关接入 Sentinel 与 Nacos 数据源
ruoyi-gateway-dev.yml
(节选)spring:cloud:sentinel:eager: truetransport:dashboard: 127.0.0.1:8718datasource:gw-api:nacos:server-addr: localhost:8848group-id: DEFAULT_GROUPnamespace: publicdata-id: ruoyi-gateway-gw-api-defsdata-type: jsonrule-type: gw-api-groupgw-flow:nacos:server-addr: localhost:8848group-id: DEFAULT_GROUPnamespace: publicdata-id: ruoyi-gateway-gw-flowdata-type: jsonrule-type: gw-flow
3)验证
# 连续高频请求
curl -i http://localhost:8080/external/gs/robots
curl -i http://localhost:8080/external/gs/robots
命中限流时:HTTP/1.1 500 Too Many Requests,终端能看到 Blocked by Sentinel (flow limiting)。
Sentinel 控制台:在 ruoyi-gateway 应用下,请求链路 / API 管理 都能看到 external-gs-api 的统计与规则。
Sentinel 限流常见问题与修复方案
FAQ 1:Code200,401令牌不能为空
症状
-
压测的时候报Code200,401令牌不能为空
根因
-
ruoyi-gateway-dev.yml中验证码没关
-
没有为你的自定义API设置白名单
修复
-
captcha中把enabled改为:false
-
在ignore中为你的API设置白名单
FAQ 2:控制台看不到实例 / 资源列表是空的
症状
-
Sentinel 控制台没有
ruoyi-gateway
或ruoyi-robot
实例 -
或者“资源链路”里没有 URL/方法名
根因
-
Dashboard 端口不一致 / 服务没连上
-
Sentinel 默认懒加载,未触发调用不汇报
-
依赖缺失 / 网段不通
修复
-
application-*-dev.yml
开启:
spring:cloud:sentinel:eager: truetransport:dashboard: 127.0.0.1:8718
启动 Dashboard(端口一致):
java -Dserver.port=8718 \-Dcsp.sentinel.dashboard.server=localhost:8718 \-Dcsp.sentinel.api.port=8719 \-jar sentinel-dashboard-1.8.x.jar
打一次接口触发资源上报:
curl -i http://localhost:8080/external/gs/robots
FAQ 3:Nacos 已配规则,但网关不生效
症状
-
控制台能看到实例,但限流不触发,或“API 管理”看不到你的分组。
根因
-
DataId / group / namespace /
rule-type
/data-type
任一不匹配 -
JSON 不合法
-
你自定义的API类在Nacos没有设置Sentinel
修复清单
-
ruoyi-gateway-dev.yml
的数据源必须与 Nacos 完全一致: -
gw-api-defs
与gw-flow
的apiName
/resource
要对应(你用的是external-gs-api
)。datasource:gw-api:nacos:server-addr: localhost:8848group-id: DEFAULT_GROUPnamespace: publicdata-id: ruoyi-gateway-gw-api-defsdata-type: jsonrule-type: gw-api-groupgw-flow:nacos:server-addr: localhost:8848group-id: DEFAULT_GROUPnamespace: publicdata-id: ruoyi-gateway-gw-flowdata-type: jsonrule-type: gw-flow
-
以ruoyi-robot-dev.yml为例,增加配置
spring:cloud:sentinel:# 取消控制台懒加载eager: truetransport:# 控制台地址dashboard: 127.0.0.1:8718
FAQ 4:我压不出 429,是不是没生效?
可能原因
-
阈值太高 / 压测强度不够
-
burst
、intervalSec
没理解
修复
-
先把阈值设成
count: 1
、intervalSec: 1
做冒烟。 -
命令行快速压测:
# Linux/Mac
for i in {1..10}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/external/gs/robots; done# Windows(PowerShell)
1..10 | % { (Invoke-WebRequest -UseBasicParsing http://localhost:8080/external/gs/robots).StatusCode }
FAQ 5:怎么区分 B 与 C
1.看控制台“API类型”这一列
-
C(API 分组/Nacos 下发):在 ruoyi-gateway → API管理 必须能看到
external-gs-api
;对应的流控规则里“API 类型”显示 自定义API,API 名称就是external-gs-api
。 -
B(Route ID 临时规则/控制台添加):在 ruoyi-gateway → 流控规则 里“API 类型”显示 Route ID,比如
ruoyi-robot
(你的第二张图就是这个)。
2.看“重启后的持久性”
-
C:重启 gateway 后,规则会 自动重新出现(来自 Nacos 的
gw-api-defs
/gw-flow
)。 -
B:如果你只是控制台临时添加,重启就没了(除非你的控制台本身配置了 Nacos Publisher,把它也持久化到 Nacos——多数环境没配)。
3.看配置是否存在
-
C 必须有 gateway 里的这段 datasource 配置 + Nacos 中的两个 DataId:
ruoyi-gateway-gw-api-defs
(API 分组)和ruoyi-gateway-gw-flow
(分组流控)。 -
B 不需要这两个 DataId,单靠控制台在 “流控规则(Route ID)” 新增就能生效。
4.看“API管理”页面是否有分组
-
C:
external-gs-api
一定会出现在 API管理 列表里。 -
B:API管理可以是空的(就像你第一张图),依然能在“流控规则”里对 Route ID 限流。