Sentinel 限流熔断与系统防护实战
Sentinel是什么
Sentinel(面向流量防护的轻量级中间件)是 Spring Cloud Alibaba 生态中负责流量控制、熔断降级和系统负载保护的组件。它解决的核心问题是:微服务之间互相调用时,一个上游的流量尖刺或下游的故障如何不拖垮整个系统。
简单说:Nacos 管"服务在哪里",Sentinel 管"服务能不能抗住"。
为什么需要
| 问题 | 没有防护的后果 | Sentinel 怎么做 |
|---|---|---|
| 突发流量 | 瞬间打满 CPU/内存,服务雪崩 | 限流(直接拒绝 / 排队等待 / 预热) |
| 下游服务变慢 | 上游线程全部阻塞,连锁故障 | 熔断降级(快速失败 + fallback) |
| 系统负载过高 | 服务响应全面退化 | 系统自适应保护(Load / CPU / RT 自动兜底) |
Sentinel 整体架构
限流(Flow Control)
核心概念
资源 — Sentinel 保护的最小单位,可以是方法、接口或代码块。通过 @SentinelResource 注解定义,或自动适配 Spring MVC 的每个端点。
规则 — 定义"怎么保护资源",由控制台配置或代码预先定义。
限流算法
| 算法 | 效果 | 适合场景 |
|---|---|---|
| 直接拒绝(快速失败) | 超出 QPS 阈值直接返回限流异常 | 大多数 API 接口 |
| Warm Up(冷启动) | 阈值从 阈值/coldFactor 逐步升至设定值 |
系统刚启动或长时间空闲后的流量恢复 |
| 排队等待(匀速通过) | 请求排队匀速处理,超出队列超时时间直接失败 | 消息处理、削峰填谷 |
流控模式
关联模式举例: 支付接口(/pay)和订单查询(/order/query)共享数据库连接池。设置 /pay 为关联资源,当 /pay 的 QPS 超阈值时,直接限流 /order/query,保护数据库连接不被 /pay 的慢查询耗尽。这是"放弃非核心保核心"的典型策略。
熔断降级(Circuit Breaking)
熔断器状态机
三种熔断策略
| 策略 | 阈值含义 | 统计维度 | 适用场景 |
|---|---|---|---|
| 慢调用比例 | 最大 RT(如 > 500ms 就是慢调用) | 时间窗口内慢调用占比 | 下游偶尔变慢的场景 |
| 异常比例 | 异常数/总请求数(如 > 0.5 即 50%) | 异常占比 | 依赖的下游不稳定 |
| 异常数 | 时间窗口内异常总数(如 > 10 次) | 绝对次数 | 异常稀疏但绝对数量可观的场景 |
熔断后的恢复路径:
- 熔断器 OPEN → 所有请求直接降级走 fallback
- 经过熔断时长(如 5 秒)→ 进入 HALF-OPEN
- HALF-OPEN 放行一个探测请求 → 成功?回到 CLOSED。失败?回到 OPEN
系统自适应保护(System Protection)
Sentinel 从全局入口(所有流量进入应用的第一层)出发做的保护,不是针对单个资源,而是保护整个应用的极限水位。
| 维度 | 说明 |
|---|---|
| Load | 当系统 Load1(1 分钟平均 Load)超过阈值,且当前并发线程数超过预估容量时触发 |
| CPU 使用率 | 当 CPU 使用率超过阈值时触发限流 |
| 平均 RT | 当所有入口流量的平均 RT 超过阈值时触发 |
| 并发线程数 | 当入口流量并发线程数超过阈值时触发 |
| 入口 QPS | 当所有入口流量的 QPS 超过阈值时触发 |
和限流的区别: 限流是单资源维度的精准控制;系统保护是整体维度的粗粒度兜底——就像家里的每个电器有自己的保险丝,但总闸也有一个过载保护。
新人上手指南
第一步:启动 Sentinel 控制台
# 下载(当前稳定版 1.8.8)
curl -O https://github.com/alibaba/Sentinel/releases/download/1.8.8/sentinel-dashboard-1.8.8.jar
# 启动(默认运行在 8080 端口)
java -jar sentinel-dashboard-1.8.8.jar
# 访问 http://localhost:8080 默认账号密码: sentinel / sentinel
Sentinel 控制台本质是一个 Spring Boot 应用,用于展示实时监控和配置规则。
生产环境请参考官方文档加 -Dserver.port 和 -Dauth.username/password 参数。
第二步:客户端接入 Sentinel
1. 添加依赖:
<!-- Spring Cloud Alibaba Sentinel 启动器 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2. 配置 application.yml:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 # 控制台地址
port: 8719 # 客户端与控制台通信端口(默认 8719)
# OpenFeign 整合:开启 Feign 的 Sentinel 支持
feign:
sentinel:
enabled: true
3. 启动后访问任意接口:
访问几次业务接口,控制台就能看到实时监控。Sentinel 采用懒加载策略——必须有一次真实请求后,资源才会出现在控制台。
第三步:配置规则
方式一:控制台配置(推荐开发调试时使用)
登陆控制台 → "簇点链路" → 找到对应资源 → "+流控" 或 "+熔断"。配置后立刻生效。
方式二:注解 + 代码配置(推荐生产使用,规则持久化)
@RestController
@RequestMapping("/order")
public class OrderController {
@GetMapping("/create")
@SentinelResource(
value = "createOrder", // 资源名称
blockHandler = "createOrderBlock", // 限流/熔断后的兜底方法
blockHandlerClass = OrderBlockHandler.class,
fallback = "createOrderFallback" // 业务异常时的兜底方法
)
public Result createOrder(@RequestParam Long userId) {
// 正常业务逻辑
if (userId == null) {
throw new IllegalArgumentException("用户 ID 不能为空");
}
return Result.ok("订单创建成功");
}
}
// 兜底处理类(方法必须为 public static,入参与原方法一致 + BlockException)
public class OrderBlockHandler {
public static Result createOrderBlock(Long userId, BlockException e) {
return Result.error("系统繁忙,请稍后再试");
}
public static Result createOrderFallback(Long userId, Throwable t) {
return Result.error("业务异常: " + t.getMessage());
}
}
@SentinelResource 关键属性速查:
| 属性 | 作用 | 触发条件 |
|---|---|---|
| value | 资源名称,控制台以此识别 | 必填 |
| blockHandler | 被限流/熔断时的兜底 | BlockException 触发 |
| fallback | 业务代码抛出异常时 | 非 BlockException 的异常 |
| defaultFallback | 通用兜底(未指定 blockHandler/fallback 时生效) | 所有异常 |
| exceptionsToIgnore | 指定忽略的异常类型,不触发 fallback | 特定异常 |
blockHandler vs fallback 的区别: blockHandler 处理的是 Sentinel 自身的限流/熔断异常(BlockException),fallback 处理的是业务代码抛出的异常。两者可以共存,互不冲突。
常见问题
1. 控制台看不到服务
- 确认
spring.cloud.sentinel.transport.dashboard配置正确 - 访问一次任意接口后再刷新(懒加载)
- 检查防火墙:控制台 8080 和客户端 8719 端口正常通信
- 如果用了 Spring Cloud Gateway,需引入
spring-cloud-alibaba-sentinel-gateway依赖
2. 规则配置后不生效
- 控制台配置的规则保存在内存中,服务重启后丢失。生产环境需配合 Nacos/Apollo 做规则持久化
- 资源名不一致:控制台的资源名必须和
@SentinelResource(value = "...")一致 - blockHandler 方法签名不匹配:返回值、参数列表(含 BlockException)必须与原方法一致
3. @SentinelResource 限流了但没走 blockHandler
最常见的原因是 blockHandler 方法签名不对:
// ❌ 错误:缺少 BlockException 参数
public static String createOrderBlock(Long userId) { ... }
// ✅ 正确:参数必须与原方法一致 + BlockException 放在最后
public static String createOrderBlock(Long userId, BlockException e) { ... }
4. Sentinel 和 Hystrix 的区别(面试常问)
| 维度 | Sentinel | Hystrix(已停更) |
|---|---|---|
| 隔离策略 | 信号量隔离 | 线程池/信号量隔离 |
| 限流能力 | 基于 QPS/线程数 + 流量整形(Warm Up、排队等) | 有限支持 |
| 熔断策略 | 慢调用比例、异常比例、异常数 | 基于失败比例 |
| 系统自适应保护 | ✅ 支持 | ❌ 不支持 |
| 实时监控 | ✅ 开箱即用控制台 | ⚠️ 需要整合 Turbine |
| 规则持久化 | SPI 扩展,支持 Nacos/Apollo | ❌ 无原生支持 |
| 活跃状态 | ✅ 持续维护 | ❌ 2018 年停更 |
面试题
Q1:Sentinel 的限流和熔断有什么区别?什么时候该用哪个?
参考答案:
限流保护的是自身,熔断保护的是自身不被下游拖垮——这是两者最本质的区别。
限流:当流量超过自身处理能力时主动拒绝请求,防止服务被击垮。触发条件是"流量 > 阈值"(QPS 或线程数)。适用于能预测自身容量边界的场景。
熔断:当下游(或被调用的依赖)出现故障或响应变慢时,主动切断对其的调用链路,快速失败而不是长时间等待。触发条件是"下游的异常/慢调用比例超过阈值"。适用于服务间调用场景(A 调 B,B 变慢时熔断对 B 的调用)。
两者可以同时存在——比如一个订单接口可以既设置 QPS 限流(保护自身),又设置针对下游用户服务的熔断规则(防止用户服务故障拖垮订单服务)。
对比:
| 限流 | 熔断 | |
|---|---|---|
| 保护对象 | 自身不被入流量压垮 | 自身不被故障下游拖垮 |
| 触发条件 | 流量超过设定阈值 | 下游异常比例/占比超过阈值 |
| 恢复方式 | 流量降下来自动恢复 | 熔断时长后进入 HALF-OPEN 探测恢复 |
| 典型场景 | 秒杀、大促 | 下游服务慢/故障 |
Q2:Sentinel 控制台配置的规则重启后就没了,怎么解决?
参考答案:
Sentinel 控制台的规则是存在内存中的,服务重启或控制台重启都会丢失。生产环境必须做规则持久化。有两种主流方案:
方案一:Nacos 作为规则数据源(推荐)
Sentinel 提供了 SPI 扩展,从 Nacos 中拉取规则配置,控制台修改规则时同步写入 Nacos。
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
spring:
cloud:
sentinel:
datasource:
flow: # 流控规则
nacos:
server-addr: localhost:8848
data-id: sentinel-flow-rules
rule-type: flow # 规则类型:flow / degrade / system 等
degrade: # 熔断规则
nacos:
server-addr: localhost:8848
data-id: sentinel-degrade-rules
rule-type: degrade
在 Nacos 中创建对应的 DataId,配置 JSON 格式的规则内容。这样规则配置和业务配置一样,放在同一个配置中心统一管理。
方案二:启动时代码加载规则
@PostConstruct
public void initFlowRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("createOrder");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100); // 100 QPS
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
两种方案的选择:Nacos 持久化适合需要动态调整规则的场景;代码加载适合规则相对固定的场景,改规则需要重启服务。推荐用 Nacos 方案。
Q3:一个请求被 Sentinel 限流后,客户端会收到什么?如何做友好的限流提示?
参考答案:
默认情况下,被 Sentinel 限流后会抛 BlockException,Spring Boot 默认返回 429(Too Many Requests)状态码 + 简单的错误信息。这对用户不友好。
有两种方式做自定义限流提示:
方式一:@SentinelResource 的 blockHandler(推荐,精细控制)
每个资源指定自己的 blockHandler 方法,返回友好的业务错误信息。如前面的例子所示,返回 Result.error("系统繁忙,请稍后再试") 替代默认的异常信息。
方式二:全局的 BlockExceptionHandler(统一兜底)
实现一个全局处理器,所有未在 @SentinelResource 中指定 blockHandler 的资源都会走这里:
@Component
public class MySentinelBlockHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
BlockException e) throws Exception {
response.setStatus(429);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(
"{\"code\":429, \"msg\":\"系统繁忙,请您稍后再试\"}"
);
}
}
两种方式的职责边界:blockHandler 适合"我知道这个接口被限流时该返回什么"的场景;全局 handler 适合"我不知道哪些接口被限流,但要统一保证不返回原始异常"的场景。实践中可以先用全局 handler 兜底,再对核心接口逐个加 @SentinelResource 做精细化的提示。
延伸阅读: Sentinel 官方 Wiki https://github.com/alibaba/Sentinel/wiki 和技术博客《阿里巴巴 Sentinel 技术解密》,里面详细解释了滑动窗口和限流算法的实现细节。

浙公网安备 33010602011771号