場景:
-
服務:
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 限流。