摘要:本文从微服务为什么需要限流与熔断讲起,系统介绍 Sentinel 的流控规则、熔断降级规则、热点参数限流与系统保护等核心配置策略;深入对比固定窗口、滑动窗口、漏桶、令牌桶四大限流算法的原理、优缺点与适用场景,并给出每种算法在 Sentinel 中的对应实现,最后附上实战配置建议。
一、为什么需要限流与熔断
随着微服务架构的普及,服务之间的调用链路越来越长:一个请求可能要经过网关、用户服务、订单服务、库存服务等多个节点。任何一个节点的抖动都可能被放大,最终引发服务雪崩(Cascade Failure)。
举个常见的例子:
- 用户服务 A 调用订单服务 B,B 响应变慢;
- A 的线程池被慢请求占满,A 自己也变慢;
- 调用 A 的上游服务 C 随之被拖垮;
- 雪崩就这样沿着调用链一路传播下去。
应对雪崩有三板斧:
- 限流(Rate Limiting):控制请求的进入速度,把流量挡在系统容量之外;
- 熔断(Circuit Breaking):下游不可用时快速切断调用,避免无限等待;
- 降级(Degradation):提供兜底逻辑,保证核心功能可用、非核心功能可牺牲。
Sentinel 正是阿里巴巴开源的一款以"流量"为切入点的稳定性组件,从流量控制、熔断降级、系统负载保护等多个维度保障服务的稳定性,并且经历了双十一大促等核心场景的长期考验。
二、Sentinel 核心概念与整体架构
2.1 两个组成部分
Sentinel 分为两部分:
| 组成部分 | 说明 |
|---|---|
| 核心库(Java 客户端) | 不依赖任何框架,可运行于所有 Java 运行时环境,对 Spring Cloud、Dubbo、gRPC 等提供良好支持 |
| 控制台(Dashboard) | 基于 Spring Boot 开发,开箱即用,提供实时监控、机器发现、规则管理等能力 |
2.2 资源与规则
Sentinel 的核心思想是:开发者只需要关注"资源"的定义,规则可以随时动态追加。
- 资源(Resource):被 Sentinel 保护的入口,可以是接口、方法、甚至一段代码;
- 规则(Rule):定义如何保护资源,包括流控规则、熔断规则、热点规则、系统规则、授权规则等。
规则可以通过控制台在线配置,也可以通过代码 API 加载,还可以接入 Nacos、Apollo 等配置中心实现动态持久化。
2.3 Sentinel 与 Hystrix 对比
| 能力 | Sentinel | Hystrix |
|---|---|---|
| 隔离策略 | 信号量隔离 | 线程池隔离 / 信号量隔离 |
| 熔断策略 | 响应时间、慢调用比例、异常比例、异常数 | 基于失败比率 |
| 实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
| 限流能力 | 基于 QPS,支持调用关系限流 | 有限支持 |
| 流量整形 | 慢启动(Warm Up)、匀速排队 | 不支持 |
| 系统负载保护 | 支持 | 不支持 |
| 控制台 | 开箱即用,监控、规则、机器发现齐全 | 不完善 |
| 常见框架适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
三、限流算法详解与对比
这是本文的重点。限流算法决定了"什么时候拒绝、拒绝多少、流量是否平滑",理解它们才能选对配置策略。
3.1 固定窗口算法(计数器算法)
原理:
将时间划分为固定长度的窗口(比如 1 秒),每个窗口内维护一个计数器:
- 请求进来,计数器 +1;
- 计数器超过阈值,直接拒绝;
- 窗口结束,计数器清零,进入下一个窗口。
text
窗口1(0~1s) 窗口2(1~2s)
[|||||||] 7个请求 [|||||||] 7个请求
阈值 = 5,窗口2内第6个请求被拒绝
优点:
- 实现极其简单,内存占用小;
- 性能好,适合高并发下的粗略限流。
缺点:
存在著名的临界问题:在窗口边界处,两个窗口内的请求可以瞬间"合流",造成 2 倍阈值的突发流量。
text
阈值 = 100 QPS
0.9s~1.0s 通过 100 个请求
1.0s~1.1s 又通过 100 个请求
→ 0.2s 内实际通过了 200 个请求!
适用场景:
- 对精度要求不高、实现优先的简单限流;
- 统计类的粗略计数场景。
3.2 滑动窗口算法
原理:
滑动窗口是固定窗口的改进版:把窗口细分成 N 个小格子(样本窗口),每个格子独立计数,统计时滑动当前窗口并累加所有格子的计数。
text
时间轴: |0.0|0.1|0.2|0.3|0.4|0.5|0.6|0.7|0.8|0.9| (秒)
窗口向右滑动,始终统计最近 1 秒内的所有格子之和
格子切得越细,统计越平滑,逼近真正的"滑动时间窗"。
优点:
- 解决了固定窗口的临界突发问题;
- 精度可控(格子越细精度越高)。
缺点:
- 实现复杂度高于固定窗口;
- 内存占用随格子数量增加。
适用场景:
- 通用接口限流,是最普适的方案;
- 秒杀、热点数据等对瞬时突发敏感的流量防护。
Sentinel 对应 :Sentinel 默认的限流统计就是基于滑动窗口实现的(DefaultController,即"快速失败"模式)。
3.3 漏桶算法(Leaky Bucket)
原理:
想象一个底部有洞的桶:
- 请求像水一样倒入桶中;
- 桶以固定速率向外漏出(处理请求);
- 桶满了之后,新来的请求直接丢弃。
text
请求进来 ──► ╭─────────╮
│ 桶(队列)│ ──► 固定速率处理
桶满则丢弃 ╰─────────╯
优点:
- 输出速率绝对恒定,是最好的"流量整形"工具;
- 能有效削峰填谷,保护下游系统。
缺点:
- 无法应对突发流量:即使桶是空的,也不能加速处理积压请求;
- 突发流量会排队等待,可能增大响应延迟。
适用场景:
- 对接有明确 QPS 限制的第三方 API;
- 消息消费、数据库写入等需要严格控制速率、平滑流量的场景;
- 削峰填谷类业务(如秒杀下单后异步落库)。
Sentinel 对应 :流控效果中的**排队等待(匀速排队)**就是漏桶算法的实现(RateLimiterController)。
3.4 令牌桶算法(Token Bucket)
原理:
系统以固定速率向桶中放入令牌,桶有容量上限:
- 请求到达时,从桶中取一个令牌,取到则放行;
- 取不到令牌则拒绝(或等待);
- 桶中积攒的令牌可以被一次性消费,因此允许一定程度的突发。
text
令牌以 r 个/秒的速率放入 ──► ╭─────────╮
│ 令牌桶 │ ◄── 请求来取令牌,取到放行
╰─────────╯
桶容量 = b,最多允许 b 个突发
优点:
- 兼顾平均速率限制 与突发流量容忍,非常灵活;
- 是业界最常用的限流算法之一(如 Guava RateLimiter、Spring Cloud Gateway 的 RequestRateLimiter)。
缺点:
- 实现相对复杂;
- 突发流量可能超过预期(burst 上限需要仔细调参)。
适用场景:
- 允许短暂突发、但长期平均速率受限的场景,如 Web 入口、API 网关;
- 需要"冷启动预热"的场景(系统刚启动时逐步放开流量)。
Sentinel 对应 :流控效果中的 Warm Up(预热) 参考了 Guava 令牌桶实现(WarmUpController),热点参数限流也使用了令牌桶思路。
3.5 四大算法对比总结
| 算法 | 实现复杂度 | 允许突发 | 流量平滑 | 临界问题 | 典型应用 |
|---|---|---|---|---|---|
| 固定窗口 | 低 | 差(窗口边界可翻倍) | 差 | 有 | 粗略计数、简单限流 |
| 滑动窗口 | 中 | 中 | 中 | 基本解决 | 通用接口限流、秒杀防护 |
| 漏桶 | 中 | 无 | 强(恒定速率) | 无 | 削峰填谷、对接第三方 API |
| 令牌桶 | 中 | 允许(有上限) | 中 | 无 | 网关入口、平均限速+突发 |
3.6 算法选择建议
- 不知道选什么:默认用滑动窗口(Sentinel 默认快速失败),能覆盖 90% 的接口限流场景;
- 流量必须绝对平滑:选漏桶(排队等待),保护下游数据库、第三方接口;
- 允许突发但要限平均速率:选令牌桶(Warm Up / 自定义),适合网关与入口;
- 对边界突发零容忍:避免固定窗口,改用滑动窗口或更细的窗口。
四、Sentinel 限流配置策略
4.1 快速接入
- 引入依赖:
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
- 下载并启动控制台:
bash
java -jar sentinel-dashboard-1.8.3.jar
默认地址 http://localhost:8080,账号密码均为 sentinel。
- 配置控制台地址:
yaml
spring:
application:
name: mdx-shop-user
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080 # Sentinel Dashboard 地址
feign:
sentinel:
enabled: true
访问一次服务接口后,控制台即可看到监控到的资源。
4.2 流控规则属性
| 属性 | 说明 |
|---|---|
| 资源名 | 资源的唯一名称,默认是请求路径 |
| 针对来源 | 针对调用方限流,填写微服务名,默认 default(不区分来源) |
| 阈值类型 | QPS(每秒请求数)或 并发线程数 |
| 单机阈值 | 达到该阈值即触发限流 |
| 是否集群 | 是否启用集群流控 |
| 流控模式 | 直接 / 关联 / 链路 |
| 流控效果 | 快速失败 / Warm Up / 排队等待 |
4.3 三种流控模式
1. 直接(默认)
资源自身的 QPS/线程数达到阈值,直接限流。适合对单个接口做能力上限保护。
2. 关联
当关联资源达到阈值时,限流自己。典型场景:
text
读接口 /order/read
写接口 /order/write ← 给读接口配置"关联"到写接口
当写接口 QPS 过高(数据库压力大)时,自动限流读接口,保护数据库。
3. 链路
只统计从指定入口资源进入的流量。适合针对来源做更精细的限流,例如同一个方法被多个上游调用时,只限制来自某个入口的流量。
4.4 三种流控效果
流控效果决定了触发阈值后请求如何处理,每种效果对应一种算法:
| 流控效果 | 对应算法 | 行为 |
|---|---|---|
| 快速失败 | 滑动窗口 | 达到阈值后直接拒绝,抛出 FlowException,默认效果 |
| Warm Up | 令牌桶(预热) | 冷启动时阈值从 阈值/coldFactor(默认 3)开始,经过预热时长逐步达到设定阈值,适合系统冷启动 |
| 排队等待 | 漏桶 | 请求以均匀速度通过,超时时间内排队,超过则拒绝;只支持 QPS 阈值类型,可配置排队超时时间 |
Warm Up 示意图:
text
QPS
│ ┌──── 目标阈值(如 100)
│ ┌─┘
│ ┌──┘
│ ┌──┘
│ ┌───────┘ ← 预热期内从 100/3 ≈ 33 逐步上升
└─────────────────────────────► 时间
0 预热时长(如 10s)
4.5 代码方式配置流控规则
除了控制台,还可以通过代码加载规则,适合测试与自动化场景:
java
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import java.util.Collections;
public class FlowRuleConfig {
public static void initFlowRules() {
FlowRule rule = new FlowRule();
rule.setResource("getOrderNoResource"); // 资源名
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 阈值类型:QPS
rule.setCount(10); // 单机阈值:每秒最多 10 个请求
rule.setLimitApp("default"); // 针对来源:不区分来源
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); // 快速失败
// 排队等待示例:匀速放行,最大排队 500ms
// rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
// rule.setMaxQueueingTimeMs(500);
FlowRuleManager.loadRules(Collections.singletonList(rule));
}
}
4.6 @SentinelResource 注解与兜底处理
用注解定义资源,并配置限流/熔断的兜底方法:
java
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("getOrderNo")
@SentinelResource(
value = "getOrderNoResource",
blockHandler = "getOrderNoBlockHandler",
blockHandlerClass = UserController.class
)
public String getOrderNo(String userId, String tenantId, HttpServletRequest request) {
return userService.getOrderNo(userId, tenantId, request);
}
/**
* 限流兜底方法:返回类型与原方法一致,
* 参数与原方法匹配,最后追加 BlockException 参数。
*/
public static String getOrderNoBlockHandler(String userId, String tenantId,
HttpServletRequest request,
BlockException e) {
return "不好意思,前方拥挤,请您稍后再试";
}
}
注解常用属性:
| 属性 | 说明 |
|---|---|
value |
资源名称,必填 |
entryType |
流量方向,IN / OUT,默认 OUT |
blockHandler |
处理 BlockException(限流、熔断触发)的方法名 |
blockHandlerClass |
blockHandler 所在类,跨类时该方法必须 static |
fallback |
处理业务异常的方法名(可带 Throwable 参数) |
fallbackClass |
fallback 所在类,跨类时必须 static |
defaultFallback |
通用兜底,fallback 优先于它 |
exceptionsToIgnore |
指定忽略的异常,不统计、不进兜底,原样抛出 |
注意:
blockHandler处理的是 Sentinel 触发的BlockException(限流/熔断),fallback处理的是业务方法自身抛出的异常,两者职责不同。
五、Sentinel 熔断降级配置策略
5.1 熔断器状态机
Sentinel 的熔断器有三个状态,与主流熔断框架(如 Hystrix、Resilience4j)一致:
#mermaid-svg-HPWE84taoXdvuLAB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HPWE84taoXdvuLAB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HPWE84taoXdvuLAB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HPWE84taoXdvuLAB .error-icon{fill:#552222;}#mermaid-svg-HPWE84taoXdvuLAB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HPWE84taoXdvuLAB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HPWE84taoXdvuLAB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HPWE84taoXdvuLAB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HPWE84taoXdvuLAB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HPWE84taoXdvuLAB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HPWE84taoXdvuLAB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HPWE84taoXdvuLAB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HPWE84taoXdvuLAB .marker.cross{stroke:#333333;}#mermaid-svg-HPWE84taoXdvuLAB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HPWE84taoXdvuLAB p{margin:0;}#mermaid-svg-HPWE84taoXdvuLAB defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-HPWE84taoXdvuLAB g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-HPWE84taoXdvuLAB g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-HPWE84taoXdvuLAB g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-HPWE84taoXdvuLAB g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-HPWE84taoXdvuLAB g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-HPWE84taoXdvuLAB .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-HPWE84taoXdvuLAB .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-HPWE84taoXdvuLAB .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-HPWE84taoXdvuLAB .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-HPWE84taoXdvuLAB .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-HPWE84taoXdvuLAB .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-HPWE84taoXdvuLAB .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-HPWE84taoXdvuLAB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HPWE84taoXdvuLAB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HPWE84taoXdvuLAB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HPWE84taoXdvuLAB .edgeLabel .label text{fill:#333;}#mermaid-svg-HPWE84taoXdvuLAB .label div .edgeLabel{color:#333;}#mermaid-svg-HPWE84taoXdvuLAB .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-HPWE84taoXdvuLAB .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-HPWE84taoXdvuLAB .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-HPWE84taoXdvuLAB .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-HPWE84taoXdvuLAB .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-HPWE84taoXdvuLAB .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HPWE84taoXdvuLAB .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HPWE84taoXdvuLAB #statediagram-barbEnd{fill:#333333;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HPWE84taoXdvuLAB .cluster-label,#mermaid-svg-HPWE84taoXdvuLAB .nodeLabel{color:#131300;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-HPWE84taoXdvuLAB .note-edge{stroke-dasharray:5;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-note text{fill:black;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram-note .nodeLabel{color:black;}#mermaid-svg-HPWE84taoXdvuLAB .statediagram .edgeLabel{color:red;}#mermaid-svg-HPWE84taoXdvuLAB #dependencyStart,#mermaid-svg-HPWE84taoXdvuLAB #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-HPWE84taoXdvuLAB .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HPWE84taoXdvuLAB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 统计周期内指标超过阈值
熔断时长结束
探测请求成功
探测请求失败
CLOSED
OPEN
HALF_OPEN
- CLOSED(关闭):正常放行,持续统计指标;
- OPEN(打开):熔断生效,请求直接降级,不再调用下游;
- HALF_OPEN(半开):熔断时长结束后放行一个探测请求,成功则恢复,失败则再次熔断。
5.2 三种熔断策略
1. 慢调用比例(SLOW_REQUEST_RATIO)
- 设置允许的最大 RT(慢调用阈值);
- 统计时长内请求数超过最小请求数,且慢调用比例超过阈值,则触发熔断;
- 恢复后放行一个探测请求,若其 RT 小于慢调用阈值则恢复,否则再次熔断。
2. 异常比例(ERROR_RATIO)
- 统计时长内请求数超过最小请求数,且异常比例超过阈值(范围
[0.0, 1.0])则熔断; - 探测请求成功则恢复,失败则再次熔断。
3. 异常数(ERROR_COUNT)
- 统计时长内异常数超过阈值则熔断;
- 探测请求成功则恢复,失败则再次熔断。
5.3 控制台配置步骤
以慢调用比例为例,在控制台"熔断规则 → 新增"中配置:
| 配置项 | 示例值 | 说明 |
|---|---|---|
| 资源名 | sentinelAResource |
与 @SentinelResource 的 value 一致 |
| 熔断策略 | 慢调用比例 | 三种策略之一 |
| 最大 RT | 200 ms | 响应时间超过则记为慢调用 |
| 比例阈值 | 0.1 | 慢调用占比超过 10% 触发熔断 |
| 最小请求数 | 5 | 统计周期内请求数必须大于该值才判定 |
| 统计时长 | 2 秒 | 指标统计窗口 |
| 熔断时长 | 5 秒 | 熔断器打开的时间,结束后进入半开探测 |
提示:Sentinel 默认统计的 RT 上限是 4900ms,超过部分按 4900ms 计算;如需调整,可通过启动参数
-Dcsp.sentinel.statistic.max.rt=xxx修改。
5.4 代码方式配置熔断规则
java
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import java.util.Collections;
public class DegradeRuleConfig {
public static void initDegradeRules() {
DegradeRule rule = new DegradeRule();
rule.setResource("sentinelAResource"); // 资源名
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT); // 熔断策略:慢调用比例
rule.setCount(200); // 最大 RT:200ms
rule.setSlowRatioThreshold(0.1); // 慢调用比例阈值:10%
rule.setMinRequestAmount(5); // 最小请求数
rule.setStatIntervalMs(2000); // 统计时长:2s
rule.setTimeWindow(5); // 熔断时长:5s
DegradeRuleManager.loadRules(Collections.singletonList(rule));
}
}
配套兜底方法:
java
@Component
public class UserSentinelResourceHandler {
/**
* fallback 兜底方法:方法名与 @SentinelResource 中配置一致,必须 static。
*/
public static String sentinelAResource(Throwable throwable) {
return "触发熔断,服务暂不可用,请稍后重试";
}
}
5.5 熔断策略怎么选
| 场景 | 推荐策略 |
|---|---|
| 下游响应变慢,需要按 RT 感知 | 慢调用比例 |
| 依赖方大量报错(如 5xx、业务异常) | 异常比例 |
| 异常绝对值更直观、数量稳定 | 异常数 |
| 需要兼顾慢调用与异常 | 优先慢调用比例 + 异常比例组合 |
六、进阶:热点参数限流与系统保护
6.1 热点参数限流
热点参数限流针对参数维度做限流,例如:同一个商品 ID 被大量访问时只限制该商品,而不是限制整个接口。
控制台配置要点:
- 资源名:与方法上
@SentinelResource的 value 一致; - 参数索引:第几个参数(0 开始);
- 单机阈值:单个参数值每秒最大访问量;
- 参数例外项:为特定参数值单独设置阈值。
java
@GetMapping("/hotspot")
@SentinelResource(
value = "hotspotResource",
blockHandler = "hotspotResource",
blockHandlerClass = UserSentinelResourceHandler.class
)
public String hotspot(@RequestParam(value = "userId", required = false) String userId,
@RequestParam(value = "shopId", required = false) String shopId) {
return "我是hotspot";
}
// 兜底方法
public static String hotspotResource(String userId, String shopId, BlockException e) {
return "您被认为恶意访问,触发热点限流";
}
6.2 系统保护规则
系统规则从应用入口维度进行保护,监控单台机器的:
load(系统负载,仅 Linux/Unix 生效);- CPU 使用率;
- 平均 RT;
- 入口 QPS;
- 并发线程数。
当任意指标超过阈值时,所有入口流量都会被限流,让系统"跑在最大吞吐量的同时保持稳定"。适合作为兜底保护,但注意它作用于整个应用,配置时要保守。
七、最佳实践建议
- 资源命名统一 :优先用
@SentinelResource显式命名资源,避免路径变化导致规则失效;业务兜底与异常兜底分开写(blockHandler/fallback)。 - 限流算法按场景选:通用接口用默认滑动窗口(快速失败);需要平滑流量用排队等待(漏桶);系统冷启动用 Warm Up(令牌桶)。
- 熔断阈值要基于压测:不要拍脑袋定 RT 和比例,先压测拿到正常 RT 的 P99,再设置慢调用阈值,通常取 P99 的 1.5~2 倍留出缓冲。
- 熔断时长不宜过短:至少给下游 5~10 秒恢复窗口,避免频繁开合(振荡)。
- 规则动态化:生产环境通过 Nacos/Apollo 管理规则,支持热更新与持久化,避免重启丢失。
- 先监控后限流:上线前先观察控制台监控数据,确认正常流量基线,再配置阈值。
- 限流兜底要友好:用户侧返回提示要语义明确(如"系统繁忙,请稍后再试"),并做好日志埋点便于排查。
八、总结
本文围绕 Sentinel 的限流与熔断,梳理了完整的配置策略:
- 流控规则:阈值类型(QPS/线程数)、流控模式(直接/关联/链路)、流控效果(快速失败/Warm Up/排队等待);
- 熔断规则:慢调用比例、异常比例、异常数三种策略,以及 CLOSED → OPEN → HALF_OPEN 的恢复机制;
- 热点参数限流与系统保护:参数级限流与应用级兜底。
同时对比了四大限流算法:
| 算法 | 一句话总结 | 优先选它的场景 |
|---|---|---|
| 固定窗口 | 实现最简单,但有临界突发 | 粗略限流、不计较精度 |
| 滑动窗口 | 通用且精确,Sentinel 默认方案 | 常规接口、秒杀、热点 |
| 漏桶 | 输出恒定,完美削峰填谷 | 保护第三方 API、数据库写入 |
| 令牌桶 | 限平均速率,允许突发 | 网关入口、冷启动预热 |
没有"最好"的算法,只有"最合适"的场景。理解了算法差异,再结合压测数据配置规则,Sentinel 才能真正成为服务稳定性的守护者。
本文参考了 Sentinel 官方文档整理而成。