一、為什麼需要自定義限流?基礎限流的侷限性

在第六篇基礎限流中,我們配置了 “GET:/user/get/{id}接口 QPS=20” 的規則,但實際業務場景存在更多精細化需求:

  • 場景 1:限制單個用户(如 user_id=100)每秒最多調用 3 次訂單創建接口,避免惡意刷單;
  • 場景 2:限制單個 IP(如 192.168.1.10)每秒最多訪問 10 次用户服務,防止 IP問題;
  • 場景 3:對 “訂單金額> 1000 元” 的請求單獨限流(QPS=5),優先保障小額訂單正常處理;

基礎限流(僅按接口維度)無法滿足上述需求,需通過 Sentinel 的擴展機制實現自定義限流規則,核心依賴 Sentinel 的RequestOriginParser(提取限流維度)、ParamFlowRuleManager(參數級限流)、UrlCleaner(接口路徑標準化)三大組件。

二、核心擴展機制:Sentinel 自定義限流的 3 個關鍵組件

組件

作用

適用場景

RequestOriginParser

提取請求的 “來源標識”(如 IP、用户 ID)

基於 IP、用户 ID 的限流

ParamFlowRuleManager

管理 “參數級限流規則”,支持按參數值、參數類型限流

基於接口參數(如訂單金額、商品 ID)的熱點限流

UrlCleaner

標準化接口路徑(如將/user/get/1統一為/user/get/{id})

避免因路徑參數不同導致規則重複配置

三、場景化實戰:3 類自定義限流規則實現(基於用户 / 訂單服務)

場景 1:基於 IP 的限流(限制單個 IP 的訪問頻率)

需求:用户服務的/user/get/{id}接口,單個 IP 每秒最多訪問 5 次。

步驟 1:實現 RequestOriginParser 提取 IP

在用户服務中新建com.example.user.config.SentinelIpParser.java,提取請求的真實 IP:

package com.example.user.config;import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;@Component // 必須加@Component,讓Sentinel掃描到public class SentinelIpParser implements RequestOriginParser {    @Override    public String parseOrigin(HttpServletRequest request) {        // 1. 提取真實IP(處理反向代理場景,如Nginx、Gateway)        String ip = request.getHeader("X-Real-IP");        if (ip == null || ip.isEmpty()) {            ip = request.getHeader("X-Forwarded-For");        }        if (ip == null || ip.isEmpty()) {            ip = request.getRemoteAddr(); // 直接獲取客户端IP(非代理場景)        }        // 2. 處理多IP場景(X-Forwarded-For可能返回多個IP,取第一個)        if (ip != null && ip.contains(",")) {            ip = ip.split(",")[0].trim();        }        return ip; // 返回IP作為“限流來源標識”    }}
步驟 2:配置基於 IP 的限流規則

有兩種配置方式:控制枱配置(臨時生效)和代碼配置(啓動時加載,推薦)。

方式 1:控制枱配置(快速測試)
  1. 進入 Sentinel 控制枱 → 選擇user-service → 「流控規則」→ 「新增」;
  2. 填寫規則參數(核心配置 “針對來源”):

參數

配置值

説明

資源名

GET:/user/get/{id}

接口的 Sentinel 資源名(需與監控一致)

針對來源

192.168.1.10(測試 IP)

僅限制該 IP,不填則對所有 IP 生效

限流模式

直接

對當前接口直接限流

流控效果

快速失敗

超過閾值立即返回限流提示

閾值類型

QPS

按每秒請求數限流

閾值

5

單個 IP 每秒最多 5 次請求

  1. 點擊「保存」,規則立即生效。
方式 2:代碼配置(規則持久化,推薦)

通過InitFunc接口在服務啓動時加載規則,避免控制枱配置重啓後丟失:

  1. 新建com.example.user.config.SentinelIpRuleInit.java:
package com.example.user.config;import com.alibaba.csp.sentinel.init.InitFunc;import com.alibaba.csp.sentinel.slots.block.RuleConstant;import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;import java.util.ArrayList;import java.util.List;public class SentinelIpRuleInit implements InitFunc {    @Override    public void init() throws Exception {        List<FlowRule> rules = new ArrayList<>();                // 1. 配置基於IP的限流規則(針對/user/get/{id}接口)        FlowRule ipFlowRule = new FlowRule();        ipFlowRule.setResource("GET:/user/get/{id}"); // 資源名        ipFlowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); // QPS限流        ipFlowRule.setCount(5); // 閾值5        ipFlowRule.setLimitApp("192.168.1.10"); // 限制的IP(多個IP用逗號分隔,如"192.168.1.10,192.168.1.11")        ipFlowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); // 快速失敗        rules.add(ipFlowRule);                // 2. 加載規則到Sentinel        FlowRuleManager.loadRules(rules);    }}
  1. 在resources/META-INF/services目錄下創建com.alibaba.csp.sentinel.init.InitFunc文件,指定初始化類:
com.example.user.config.SentinelIpRuleInit
  1. 重啓用户服務,規則會自動加載。
步驟 3:驗證基於 IP 的限流
  1. 用 Postman 以 IP192.168.1.10訪問http://localhost:8081/user/get/1,每秒超過 5 次請求;
  2. 超過閾值後,返回自定義限流提示(如之前配置的{"code":429,"msg":"當前請求人數過多,請稍後再試(限流)","data":null});
  3. 用其他 IP(如192.168.1.11)訪問,不受此規則限制,驗證規則僅對目標 IP 生效。

場景 2:基於用户 ID 的限流(限制單個用户的接口調用頻率)

需求:訂單服務的/order/create接口,單個用户(按user_id參數)每秒最多創建 2 個訂單。

步驟 1:實現 RequestOriginParser 提取用户 ID

在訂單服務中新建com.example.order.config.SentinelUserIdParser.java,從請求參數或 Token 中提取用户 ID:

package com.example.order.config;import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;@Componentpublic class SentinelUserIdParser implements RequestOriginParser {    @Override    public String parseOrigin(HttpServletRequest request) {        // 場景1:從請求參數提取user_id(如/create?user_id=100)        String userId = request.getParameter("user_id");                // 場景2:從Token提取user_id(實際項目推薦,需解析JWT Token)        // String token = request.getHeader("Authorization");        // if (token != null) {        //     userId = JwtUtils.getUserIdFromToken(token); // 自定義Token解析方法        // }                // 若未獲取到user_id,返回默認標識(如"anonymous")        return userId != null ? userId : "anonymous";    }}
步驟 2:配置基於用户 ID 的限流規則
  1. 控制枱配置:參考場景 1,「流控規則」中「針對來源」填寫100(用户 ID),「閾值」設為 2;
  2. 代碼配置:參考場景 1 的InitFunc,將setLimitApp("100"),資源名設為POST:/order/create。
步驟 3:驗證基於用户 ID 的限流
  1. 調用http://localhost:8082/order/create?user_id=100&amount=99.99,每秒超過 2 次請求;
  2. 超過閾值後觸發限流,用户 100 無法創建訂單,其他用户(如 user_id=101)不受影響。

場景 3:基於接口參數的熱點限流(限制特定參數值的訪問頻率)

需求:訂單服務的/order/create接口,當amount(訂單金額)>1000 元時,QPS 限制為 5;金額≤1000 元時,QPS 限制為 20(熱點參數限流)。

Sentinel 的「熱點參數限流」支持按參數值、參數索引精細化控制,需通過ParamFlowRule實現。

步驟 1:配置熱點參數限流規則(代碼方式)

在訂單服務中新建com.example.order.config.SentinelHotParamRuleInit.java:

package com.example.order.config;import com.alibaba.csp.sentinel.init.InitFunc;import com.alibaba.csp.sentinel.slots.block.RuleConstant;import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;import java.util.ArrayList;import java.util.List;public class SentinelHotParamRuleInit implements InitFunc {    @Override    public void init() throws Exception {        List<ParamFlowRule> rules = new ArrayList<>();                // 1. 配置/order/create接口的熱點參數限流規則        ParamFlowRule hotParamRule = new ParamFlowRule();        hotParamRule.setResource("POST:/order/create"); // 資源名        hotParamRule.setGrade(RuleConstant.FLOW_GRADE_QPS); // QPS限流        hotParamRule.setCount(20); // 默認閾值:金額≤1000元時QPS=20        hotParamRule.setParamIdx(1); // 參數索引:0=user_id,1=amount(按接口參數順序)        hotParamRule.setDurationInSec(1); // 統計時長1秒                // 2. 配置特殊參數值的閾值(amount>1000元時QPS=5)        List<ParamFlowItem> paramItems = new ArrayList<>();        // 注意:Sentinel熱點參數支持數值類型(int、long、double等)的範圍匹配        ParamFlowItem highAmountItem = new ParamFlowItem();        highAmountItem.setObject(double.class, 1000.0); // 參數值類型為double,閾值1000.0        highAmountItem.setCount(5); // 金額>1000元時,QPS=5        highAmountItem.setOperator(RuleConstant.PARAM_OP_GT); // 匹配條件:>(大於)        paramItems.add(highAmountItem);                hotParamRule.setParamFlowItemList(paramItems);        rules.add(hotParamRule);                // 3. 加載熱點參數規則        ParamFlowRuleManager.loadRules(rules);    }}
  1. 在resources/META-INF/services/com.alibaba.csp.sentinel.init.InFunc中添加:
com.example.order.config.SentinelHotParamRuleInit
  1. 重啓訂單服務。
步驟 2:驗證熱點參數限流
  1. 調用小額訂單(amount=500 元):http://localhost:8082/order/create?user_id=100&amount=500,每秒超過 20 次才觸發限流;
  2. 調用大額訂單(amount=1500 元):http://localhost:8082/order/create?user_id=100&amount=1500,每秒超過 5 次即觸發限流;
  3. 查看 Sentinel 控制枱「熱點參數監控」,可看到不同金額區間的 QPS 統計,驗證規則生效。

四、規則持久化:避免自定義規則重啓丟失

上述代碼配置(InitFunc)雖能在啓動時加載規則,但修改規則需重新部署服務。實際項目中推薦用Nacos 存儲 Sentinel 規則,實現 “動態配置、實時生效”,步驟如下:

1. 添加 Sentinel-Nacos 依賴(用户 / 訂單服務 pom.xml)

<dependency>    <groupId>com.alibaba.csp</groupId>    <artifactId>sentinel-datasource-nacos</artifactId>    <version>1.8.6</version> <!-- 與Sentinel版本一致 --></dependency>

2. 配置 Nacos 數據源(application.yml)

spring:  cloud:    sentinel:      datasource:        # 1. 普通流控規則(IP/用户ID限流)        flow-rule-nacos:          nacos:            server-addr: localhost:8848 # Nacos地址            data-id: sentinel-flow-rules-user-service # 數據ID(按服務區分)            group-id: SENTINEL_GROUP # 分組            data-type: json # 數據格式          rule-type: flow # 規則類型:flow(流控)、degrade(降級)、param-flow(熱點參數)        # 2. 熱點參數規則        param-flow-rule-nacos:          nacos:            server-addr: localhost:8848            data-id: sentinel-param-flow-rules-order-service            group-id: SENTINEL_GROUP            data-type: json          rule-type: param-flow

3. 在 Nacos 中創建規則配置

  1. 登錄 Nacos → 「配置管理」→ 「配置列表」→ 「新建配置」;
  2. 填寫配置(以用户服務的 IP 限流規則為例):
[  {    "resource": "GET:/user/get/{id}",    "limitApp": "192.168.1.10",    "grade": 1,    "count": 5,    "strategy": 0,    "controlBehavior": 0,    "clusterMode": false  }]

(字段説明:grade=1表示 QPS 限流,strategy=0表示直接限流,controlBehavior=0表示快速失敗);

  • 數據 ID:sentinel-flow-rules-user-service;
  • 分組:SENTINEL_GROUP;
  • 配置格式:JSON;
  • 配置內容:
  1. 發佈配置後,Sentinel 會自動拉取規則並生效,修改 Nacos 配置後無需重啓服務。

五、避坑指南:自定義限流的 4 個高頻問題

1. 坑 1:RequestOriginParser 未生效,無法提取 IP / 用户 ID

  • 現象:基於 IP 的限流規則對所有 IP 生效,或提示 “origin is empty”;
  • 原因 1:RequestOriginParser類未加@Component註解,Sentinel 未掃描到;
  • 原因 2:IP 提取邏輯錯誤(如反向代理場景未獲取X-Real-IP);
  • 解決:確認@Component註解存在,用System.out.println(ip)打印提取的 IP,驗證邏輯正確性。

2. 坑 2:熱點參數限流的 paramIdx 配置錯誤

  • 現象:熱點參數規則不生效,或限流針對錯誤的參數;
  • 原因:paramIdx與接口參數順序不匹配(如/order/create?user_id=100&amount=99.99,user_id是 0 號參數,amount是 1 號參數);
  • 解決:通過HttpServletRequest.getParameterMap()打印參數順序,確認paramIdx正確性。

3. 坑 3:Nacos 規則配置後不生效

  • 現象:修改 Nacos 中的規則,Sentinel 控制枱未更新;
  • 原因 1:application.yml中 Sentinel 數據源的rule-type配置錯誤(如熱點參數規則設為flow);
  • 原因 2:Nacos 配置的 JSON 格式錯誤(如缺少逗號、引號不匹配);
  • 解決:核對rule-type(流控 = flow,熱點參數 = param-flow),用 JSON 校驗工具(如JSON.cn)檢查格式。

4. 坑 4:基於用户 ID 限流時,匿名用户(user_id 為空)觸發所有規則

  • 現象:未傳user_id的請求被限流,即使規則針對特定用户 ID;
  • 原因:RequestOriginParser返回null,Sentinel 將null視為默認 origin,匹配所有規則;
  • 解決:未獲取到user_id時返回默認標識(如 "anonymous"),併為 "anonymous" 單獨配置寬鬆規則(如 QPS=100)。

六、與系列博客的銜接:自定義限流的應用場景擴展

本部分是對第六篇「Sentinel 熔斷降級 + 限流」的進階補充 —— 基礎限流保障接口整體穩定性,自定義限流則實現 “精細化流量控制”,兩者結合可覆蓋更多場景:

  • 結合 Gateway:在 Gateway 中實現全局 IP 限流,避免惡意流量進入業務服務;
  • 結合用户等級:對 VIP 用户配置更高限流閾值(如 VIP 用户 QPS=20,普通用户 QPS=5);
  • 結合時間窗口:在秒殺活動期間(如 10:00-10:30)臨時提高熱點商品的限流閾值。

後續若需進一步提升限流靈活性,可深入 Sentinel 的「集羣限流」(多實例共享限流閾值)、「熔斷與限流結合」(限流後觸發熔斷降級)等高級特性,確保分佈式系統在高併發場景下的穩定性。