java
static Node selectNodeByRequesterAndStrategy(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node) {
// The limit app should not be empty.
String limitApp = rule.getLimitApp();
int strategy = rule.getStrategy();
String origin = context.getOrigin();
if (limitApp.equals(origin) && filterOrigin(origin)) {
if (strategy == RuleConstant.STRATEGY_DIRECT) {
// Matches limit origin, return origin statistic node.
return context.getOriginNode();
}
return selectReferenceNode(rule, context, node);
} else if (RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp)) {
if (strategy == RuleConstant.STRATEGY_DIRECT) {
// Return the cluster node.
return node.getClusterNode();
}
return selectReferenceNode(rule, context, node);
} else if (RuleConstant.LIMIT_APP_OTHER.equals(limitApp)
&& FlowRuleManager.isOtherOrigin(origin, rule.getResource())) {
if (strategy == RuleConstant.STRATEGY_DIRECT) {
return context.getOriginNode();
}
return selectReferenceNode(rule, context, node);
}
return null;
}
这段 selectNodeByRequesterAndStrategy 方法是 Sentinel 流控规则中决定"对哪个统计节点进行限流判断" 的核心逻辑。它结合了 限流目标(limitApp) 、限流策略(strategy) 和 当前请求上下文(Context) ,动态选择一个 Node 对象用于后续的流量控制判断。
🧠 一、方法作用
返回一个 Node 对象,代表"要被限流检查的统计对象"。
- 如果返回
null→ 表示 不触发限流(直接放行)。 - 如果返回非 null → 后续用该 Node 的 QPS/线程数等指标与阈值比较。
🔑 二、关键变量说明
| 变量 | 含义 |
|---|---|
limitApp = rule.getLimitApp() |
限流规则的目标调用方: • 具体应用名(如 "appA") • default:所有来源统一限流 • other:除白名单外的所有来源 |
origin = context.getOrigin() |
当前请求的实际调用方(由上游传递,如通过 header 设置) |
strategy = rule.getStrategy() |
限流策略: • STRATEGY_DIRECT (0):直接限流 • STRATEGY_RELATE (1):关联资源限流 • STRATEGY_CHAIN (2):链路限流 |
node |
当前资源的 DefaultNode(记录该资源的总体指标) |
context.getOriginNode() |
记录 该 origin 对当前资源的访问指标(如 appA 调用 /order 的 QPS) |
node.getClusterNode() |
当前资源的全局 ClusterNode(所有 origin 汇总) |
📐 三、逻辑分支详解 + 场景举例
✅ 分支 1:limitApp == origin 且 origin 有效(非 default/other)
java
if (limitApp.equals(origin) && filterOrigin(origin)) {
if (strategy == STRATEGY_DIRECT) {
return context.getOriginNode(); // 👈 针对特定 origin 的统计
}
return selectReferenceNode(rule, context, node); // 关联/链路模式
}
✅ 场景 1.1:精准限流某个调用方(DIRECT)
- 规则:
resource="pay", limitApp="mobile", strategy=DIRECT - 请求:
origin="mobile" - 结果:返回
context.getOriginNode()→ 检查 mobile 调用 pay 的 QPS - 💡 效果:只限制 mobile 应用,web 应用不受影响。
✅ 场景 1.2:关联限流(RELATE)
- 规则:
resource="pay", limitApp="mobile", strategy=RELATE, refResource="order" - 请求:
origin="mobile" - 结果:调用
selectReferenceNode(...)→ 返回ClusterBuilderSlot.getClusterNode("order") - 💡 效果:当 order 接口的总 QPS 超过阈值,就限制 mobile 调用 pay。
注意:此时限流依据是 refResource(order)的全局流量,不是 pay 本身的流量。
✅ 分支 2:limitApp == "default"
java
else if (RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp)) {
if (strategy == STRATEGY_DIRECT) {
return node.getClusterNode(); // 👈 全局统计
}
return selectReferenceNode(rule, context, node);
}
✅ 场景 2.1:对所有调用方统一限流(DIRECT)
- 规则:
resource="pay", limitApp="default", strategy=DIRECT - 请求:
origin="web"或"mobile"或"" - 结果:返回
node.getClusterNode()→ 检查 pay 接口的总 QPS - 💡 效果:无论谁调用,只要 pay 总 QPS > 阈值,就限流。
✅ 场景 2.2:default + RELATE
- 规则:
resource="pay", limitApp="default", strategy=RELATE, refResource="user" - 结果:返回
ClusterNode("user") - 💡 效果:当 user 接口总流量过高时,限制所有调用方访问 pay。
✅ 分支 3:limitApp == "other" 且 origin 属于 "other"
java
else if (RuleConstant.LIMIT_APP_OTHER.equals(limitApp)
&& FlowRuleManager.isOtherOrigin(origin, rule.getResource())) {
if (strategy == STRATEGY_DIRECT) {
return context.getOriginNode();
}
return selectReferenceNode(rule, context, node);
}
isOtherOrigin(origin, resource)通常指:
origin不在该 resource 的白名单中(即"非信任来源")
✅ 场景 3.1:限制"非白名单"调用方
- 假设系统配置:
resource="admin-api"的白名单为["internal"] - 规则:
limitApp="other", strategy=DIRECT - 请求 A:
origin="internal"→ ❌ 不进入此分支 → 返回 null → 不限流 - 请求 B:
origin="hacker"→ ✅ 进入此分支 → 返回originNode("hacker") - 💡 效果:只限制非白名单调用者,内部系统畅通。
❌ 默认情况:返回 null
如果以上都不匹配(例如 limitApp="appA" 但 origin="appB"),则:
java
return null;
→ 不触发限流,请求直接通过。
✅ 场景 4:规则不匹配当前请求
- 规则:
limitApp="mobile" - 请求:
origin="web" - 结果:跳过该规则 → 继续检查下一条规则(或最终放行)
💡 这是 Sentinel 规则"按需生效"的体现:规则只对目标 origin 生效。
🧩 四、辅助方法 selectReferenceNode 补充说明
java
static Node selectReferenceNode(FlowRule rule, Context context, DefaultNode node) {
String refResource = rule.getRefResource();
int strategy = rule.getStrategy();
if (StringUtil.isEmpty(refResource)) return null;
if (strategy == STRATEGY_RELATE) {
return ClusterBuilderSlot.getClusterNode(refResource); // 全局关联资源节点
}
if (strategy == STRATEGY_CHAIN) {
if (!refResource.equals(context.getName())) return null; // 必须是入口资源
return node; // 当前节点(但仅在指定链路上)
}
return null;
}
场景补充:
- STRATEGY_CHAIN :只有当当前调用链的 入口资源(context.getName())等于 refResource 时才限流。
- 例:
refResource = "gateway-entry",只有从网关入口进来的链路才受限制,内部服务间调用不受限。
- 例:
✅ 五、总结:决策流程图
是 是 否 否 是 是 否 否 是 是 否 否 开始 limitApp == origin? strategy == DIRECT? 返回 originNode 调用 selectReferenceNode limitApp == 'default'? strategy == DIRECT? 返回 clusterNode limitApp == 'other' 且 origin 属于 other? strategy == DIRECT? 返回 originNode 返回 null → 不限流
💡 六、最佳实践建议
- DIRECT + specific origin:用于多租户隔离、防刷。
- DEFAULT + DIRECT:用于保护核心接口整体容量。
- RELATE:用于保护下游依赖(如 DB、第三方 API)。
- CHAIN:用于防止入口流量打垮内部服务。
- OTHER:用于安全防护(黑名单式限流)。
理解 selectNodeByRequesterAndStrategy 是掌握 Sentinel 精细化流控的关键!