Sentinel 降级与熔断
作者:白歌(飞翔科技架构师) 版本:Spring Cloud Alibaba 2025.1.x / Spring Cloud 2025.1.x / Spring Boot 4.0.x / JDK 17+
前置阅读
在开始本章之前,请确保你已完成 04_Sentinel 流量防护 的学习,掌握了 Sentinel 核心概念(资源 Resource、规则 Rule、Slot Chain 责任链)、Dashboard 部署以及基础的流控规则配置。本章聚焦熔断降级,与流控规则互补,构成 Sentinel 稳定性保障的核心双引擎。
定位
熔断降级解决什么问题
微服务架构中,一个请求往往跨越多个服务节点。当下游依赖的服务不可用或响应严重劣化时,调用方如果不做任何保护,会持续等待响应、耗尽线程池、CPU 和连接数,最终级联扩散导致整个系统雪崩。
Sentinel 的熔断降级(Circuit Breaking / Degrade)正是解决这个问题的利器:当依赖的服务出现故障时,快速失败代替无限等待,将故障隔离在调用方一侧,保护上游服务不被拖垮。
与上一章"流量控制"的区别
飞翔科技架构师白歌在一次技术评审会上,用一个比喻让新人小崔彻底记住了两者的区别:
流控是"量太大我扛不住,我自己限流"——保护的是自身服务不被流量冲垮。 熔断是"下游挂了我也不等了,我快速失败"——保护的是上游调用方不被慢依赖拖死。
| 维度 | 流量控制(Flow Control) | 熔断降级(Circuit Breaking) |
|---|---|---|
| 触发条件 | 自身 QPS / 并发线程数超过阈值 | 下游依赖的 RT、异常比例、异常数超过阈值 |
| 作用对象 | 自身服务资源 | 下游依赖的调用 |
| 保护方向 | 保护自身不被冲垮 | 保护上游不被慢依赖拖死 |
| 核心决策 | 是否放行当前请求 | 是否信任下游还能正常响应 |
| 状态模型 | 无状态(实时统计判断) | 三态状态机(CLOSED → OPEN → HALF_OPEN) |
| 恢复机制 | 流量回落后自动恢复 | 经过熔断时长后进入半开探测,探测成功才恢复 |
核心概念
熔断策略(Degrade Strategy)
Sentinel 提供四种熔断策略,各有不同的统计维度和适用场景:
| 策略 | 枚举值 | 统计口径 | 适用场景 |
|---|---|---|---|
| 慢调用比例(Slow Request Ratio) | SLOW_REQUEST_RATIO | 统计窗口内,RT(Response Time)超过阈值的慢调用占已完成调用的比例 | 检测下游服务性能劣化(数据库慢查询、网络延迟) |
| 异常比例(Error Ratio) | ERROR_RATIO | 统计窗口内,异常调用占总调用的比例 | 检测下游服务可用性下降(第三方接口故障) |
| 异常数(Error Count) | ERROR_COUNT | 统计窗口内异常调用的绝对数量 | 精确控制异常量,适合低流量服务 |
| 分钟异常数(Minute Error Count) | MINUTE_ERROR_COUNT | 分钟级统计,粒度更粗 | 分钟级别的异常监控 |
慢调用比例的核心约束:统计时只用已完成的请求(有明确 RT 的),不包含超时未返回的请求。如果请求全部超时无响应,慢调用比例统计不到,此时应配合异常比例策略或设置合理的超时时间。
熔断器状态机(Circuit Breaker State Machine)
熔断器是 Sentinel 降级的核心状态机,维护三种状态:
| 状态 | 含义 | 行为 |
|---|---|---|
| CLOSED(关闭) | 熔断器关闭,正常调用 | 所有请求正常通过,同时统计慢调用/异常指标 |
| OPEN(打开) | 熔断器打开,快速失败 | 所有请求直接抛出 DegradeException,不调用下游 |
| HALF_OPEN(半开) | 熔断器半开,探测恢复 | 允许少量请求通过(探测),成功则转 CLOSED,失败则转 OPEN |
触发条件满足(慢调用比例/异常比例/异常数超过阈值)
│
▼
CLOSED ──────────► OPEN
▲ │
│ │ 熔断时长(timeWindow)到达
│ ▼
│ HALF_OPEN
│ │ │
│ 探测成功 │ │ 探测失败
│ │ │
└───────────────┘ └─────────► OPEN
熔断规则关键参数
| 参数 | 含义 | 示例 |
|---|---|---|
resource | 资源名称 | "getOrder" |
grade | 熔断策略 | CircuitBreakerStrategy.SLOW_REQUEST_RATIO |
count | 阈值 | 慢调用比例策略下为最大 RT(ms),异常比例策略下为比例阈值(0~1) |
timeWindow | 熔断时长(秒) | 10 表示熔断 10 秒后进入 HALF_OPEN |
minRequestAmount | 最小请求数 | 5 表示统计窗口内至少要有 5 个请求才触发熔断判断 |
statIntervalMs | 统计窗口(毫秒) | 10000 表示统计最近 10 秒的指标 |
slowRatioThreshold | 慢调用比例阈值(仅慢调用比例策略) | 0.5 表示 50% 的请求是慢调用即触发熔断 |
降级处理方法
Sentinel 提供两种降级处理方法,层次分明:
| 方法 | 处理范围 | 参数特征 | 使用场景 |
|---|---|---|---|
blockHandler | BlockException(流控/熔断/系统保护等触发) | 参数与原方法一致 + BlockException | 处理限流和熔断触发的快速失败 |
fallback | 所有 Throwable(业务异常 + BlockException) | 参数与原方法一致(可选 Throwable) | 处理业务逻辑异常 + 熔断降级兜底 |
调用顺序:Sentinel 先检查规则(FlowSlot → DegradeSlot),触发 BlockException 由
blockHandler处理;规则通过后执行方法体,业务异常由fallback处理。如果fallback存在且blockHandler不存在,BlockException 也会走到fallback。
Feign 整合 Sentinel 的降级机制
Feign 整合 Sentinel 后,每次 Feign 调用会自动被 SentinelFeign 包装,作为 Sentinel 资源进行保护。当 Feign 调用失败(超时、连接拒绝、服务不可用)或触发熔断规则时,可以通过 fallback 或 FallbackFactory 返回降级结果:
| 方式 | 能力 | 适用场景 |
|---|---|---|
fallback | 返回降级结果 | 简单降级,不需要知道具体异常原因 |
FallbackFactory | 返回降级结果 + 获取异常信息 | 需要根据异常类型做不同降级处理(如记录日志、切换备用渠道) |
核心原理
熔断器三态切换(Mermaid 状态图)
Feign 调用链中 Sentinel 拦截流程(Mermaid 时序图)
Sentinel 熔断 vs Hystrix 熔断
| 维度 | Sentinel | Hystrix |
|---|---|---|
| 熔断策略 | 慢调用比例 + 异常比例 + 异常数 + 分钟异常数(4 种) | 仅支持基于 HystrixCommand 配置的单一失败率 |
| 调用隔离 | 无线程池隔离(更轻量,依赖信号量 + 快速失败) | 线程池隔离 + 信号量隔离(有线程切换开销) |
| 规则管理 | Dashboard 可视化控制台实时修改,支持动态下发 | 配置在代码或配置文件中,修改需重启 |
| 统计精度 | LeapArray 滑动窗口(默认 1s / 2 窗口) | 滑动窗口(10s / 10 个桶,每个桶 1s) |
| 规则持久化 | 原生支持推模式持久化到 Nacos / Apollo / ZooKeeper | 不内置持久化,需自行实现 |
| 半开状态 | 逐请求探测,探测成功立即恢复 | 半开后需等待一段时间才允许下一次探测 |
| 系统保护 | 支持系统 Load / CPU / RT / 线程数自适应保护 | 不支持系统级自适应保护 |
| 热点参数 | 支持热点参数限流 | 不支持 |
完整示例 1:慢调用比例熔断
场景描述
飞翔科技订单服务(小崔负责开发)通过 Feign 调用库存服务(白歌负责)查询库存。最近库存服务的数据库偶发慢查询(大表全表扫描),导致 RT 飙到 5 秒,订单服务的请求全部堆积在等待队列中,线程池耗尽后整个订单服务不可用。
大翔 CTO(技术总监)要求白歌在订单服务侧配置 Sentinel 熔断:当库存服务 RT 超过 500ms 且慢调用比例超过 50% 时,立即熔断,10 秒内快速返回降级结果,避免拖垮订单服务。
依赖配置
<!-- Sentinel 核心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
application.yml
spring:
application:
name: order-service
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080
port: 8719
eager: true
feign:
sentinel:
enabled: true
库存服务 Feign 接口
@FeignClient(
name = "stock-service",
path = "/stock",
fallbackFactory = StockFeignFallbackFactory.class
)
public interface StockFeignClient {
@GetMapping("/{productId}")
StockVO getStock(@PathVariable("productId") Long productId);
}
FallbackFactory 降级实现
@Component
public class StockFeignFallbackFactory implements FallbackFactory<StockFeignClient> {
@Override
public StockFeignClient create(Throwable cause) {
log.error("库存服务调用失败,触发降级", cause);
return productId -> {
StockVO fallback = new StockVO();
fallback.setProductId(productId);
fallback.setQuantity(0);
fallback.setMessage("库存查询暂不可用,请稍后重试");
return fallback;
};
}
}
订单服务 Controller
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private StockFeignClient stockFeignClient;
@GetMapping("/stock/{productId}")
@SentinelResource(
value = "queryStock",
blockHandler = "queryStockBlock",
fallback = "queryStockFallback"
)
public Result<StockVO> queryStock(@PathVariable Long productId) {
StockVO stock = stockFeignClient.getStock(productId);
return Result.success(stock);
}
// 熔断/限流触发时调用
public Result<StockVO> queryStockBlock(Long productId, BlockException ex) {
log.warn("queryStock 被 Sentinel 限流或熔断,productId={}", productId);
return Result.error(429, "查询过于频繁或服务暂不可用,请稍后重试");
}
// 业务异常时调用
public Result<StockVO> queryStockFallback(Long productId, Throwable ex) {
log.error("queryStock 业务异常,productId={}", productId, ex);
return Result.error(500, "服务暂时不可用");
}
}
Dashboard 配置慢调用比例规则
在 Sentinel Dashboard 中为 queryStock 资源配置熔断规则:
| 参数 | 值 | 说明 |
|---|---|---|
| 资源名 | queryStock | 对应 @SentinelResource 的 value |
| 熔断策略 | 慢调用比例 | SLOW_REQUEST_RATIO |
| 最大 RT | 500ms | 响应时间超过 500ms 算慢调用 |
| 比例阈值 | 0.5 | 50% 的请求是慢调用即触发熔断 |
| 熔断时长 | 10s | 熔断后 10 秒内所有请求快速失败 |
| 最小请求数 | 5 | 统计窗口内至少 5 个请求才判断 |
| 统计时长 | 10000ms | 统计最近 10 秒的指标 |
等价代码方式配置
@Component
public class DegradeRuleConfig implements InitializingBean {
@Override
public void afterPropertiesSet() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule("queryStock")
.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
.setCount(500) // 最大 RT 500ms
.setSlowRatioThreshold(0.5) // 慢调用比例 50%
.setMinRequestAmount(5) // 最小请求数
.setStatIntervalMs(10000) // 统计窗口 10s
.setTimeWindow(10); // 熔断时长 10s
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
}
操作前后对比
| 阶段 | 库存服务状态 | 订单服务行为 | 用户体验 |
|---|---|---|---|
| 操作前 | 数据库慢查询,RT 5s | 一直等待库存响应 → 线程池耗尽 → 新请求排队 → 全链路超时 | 页面一直转圈 → 浏览器超时报错 |
| 操作后 | 数据库慢查询,RT 5s | Sentinel 检测 50%+ 请求超过 500ms → 触发熔断 → 10s 内直接返回降级结果 | 看到友好提示"库存查询暂不可用" |
| 半开探测 | 10s 后库存可能已恢复 | HALF_OPEN 放行 1 个请求 → 探测成功 → 恢复 CLOSED | 功能自动恢复正常 |
| 探测失败 | 库存仍未恢复 | HALF_OPEN 探测请求超时 → 立即回到 OPEN → 再等 10s | 继续看到降级提示,不会雪崩 |
完整示例 2:异常比例熔断 + 备用链路切换
场景描述
飞翔科技用户服务(黄俪负责维护)调用第三方短信接口发送验证码。短信服务商偶尔故障,返回 HTTP 500 或连接超时,导致大量请求异常。黄俪在大翔 CTO 的建议下配置异常比例熔断:当短信服务异常比例超过 50% 时自动熔断 20 秒,切换至阿里云短信备用链路。
FallbackFactory 实现备用链路切换
@Service
public class SmsService {
@Autowired
private PrimarySmsClient primarySmsClient; // 主链路:某第三方短信服务商
@Autowired
private AliyunSmsClient aliyunSmsClient; // 备用链路:阿里云短信
@SentinelResource(
value = "sendVerifyCode",
blockHandler = "sendVerifyCodeBlock",
fallback = "sendVerifyCodeFallback"
)
public SmsResult sendVerifyCode(String phone) {
// 主链路发送
return primarySmsClient.send(phone, generateCode());
}
// 熔断/限流触发 → 直接切换备用链路
public SmsResult sendVerifyCodeBlock(String phone, BlockException ex) {
log.warn("主链路被 Sentinel 熔断,切换备用链路发送短信,phone={}", phone);
return aliyunSmsClient.send(phone, generateCode());
}
// 业务异常(如主链路返回异常)→ 也切换备用链路
public SmsResult sendVerifyCodeFallback(String phone, Throwable ex) {
log.warn("主链路发送异常,切换备用链路发送短信,phone={}", phone, ex);
return aliyunSmsClient.send(phone, generateCode());
}
private String generateCode() {
return String.valueOf((int) (Math.random() * 900000 + 100000));
}
}
代码方式配置异常比例规则
@Component
public class SmsDegradeRuleConfig implements InitializingBean {
@Override
public void afterPropertiesSet() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule("sendVerifyCode")
.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType())
.setCount(0.5) // 异常比例 50%
.setMinRequestAmount(5) // 最小请求数
.setStatIntervalMs(10000) // 统计窗口 10s
.setTimeWindow(20); // 熔断时长 20s
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
}
Dashboard 配置
| 参数 | 值 |
|---|---|
| 资源名 | sendVerifyCode |
| 熔断策略 | 异常比例 |
| 异常比例阈值 | 0.5 |
| 熔断时长 | 20s |
| 最小请求数 | 5 |
| 统计时长 | 10000ms |
操作前后对比
| 阶段 | 主链路状态 | 行为 | 结果 |
|---|---|---|---|
| 操作前 | 短信服务商故障,每次调用抛异常 | 异常向上传播 → 全局异常处理器返回 500 | 用户收不到验证码,页面报错 |
| 操作后 | 主链路异常比例超 50% | Sentinel 熔断 → blockHandler 自动切换到阿里云短信 | 用户无感知切换,验证码正常发送 |
| 熔断期间 | 主链路仍在故障中 | 所有请求直接走 blockHandler → 备用链路 | 100% 备用链路承载 |
| 20s 后半开 | 主链路可能已恢复 | HALF_OPEN 放行 1 个请求 → 探测成功则恢复 CLOSED | 自动切回主链路(或探测失败继续熔断) |
完整示例 3:Feign 整合熔断降级
场景描述
飞翔科技订单服务通过 Feign 调用库存服务查询库存。白歌向大翔 CTO 汇报:库存服务偶尔不可用时,Feign 调用超时抛异常,全局异常处理器返回 HTTP 500,用户看到的是"系统内部错误"。大翔要求实现 Feign 降级:库存不可用时返回默认库存为 0,并给出友好提示。
依赖与配置
spring:
application:
name: order-service
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080
port: 8719
eager: true
feign:
sentinel:
enabled: true # 开启 Sentinel 对 Feign 的拦截
client:
config:
default:
connectTimeout: 3000 # 连接超时 3s
readTimeout: 3000 # 读取超时 3s
Feign 接口定义 + FallbackFactory
@FeignClient(
name = "stock-service",
path = "/stock",
fallbackFactory = StockFeignFallbackFactory.class
)
public interface StockFeignClient {
@GetMapping("/{productId}")
StockVO getStock(@PathVariable("productId") Long productId);
@GetMapping("/batch")
List<StockVO> getStockBatch(@RequestParam("productIds") List<Long> productIds);
}
@Component
@Slf4j
public class StockFeignFallbackFactory implements FallbackFactory<StockFeignClient> {
@Override
public StockFeignClient create(Throwable cause) {
// cause 包含具体的异常信息,可用于精细化降级
if (cause instanceof RetryableException) {
log.error("库存服务连接失败,触发降级", cause);
} else if (cause instanceof FeignException.ServiceUnavailable) {
log.error("库存服务返回 503,触发降级", cause);
} else {
log.error("库存服务调用异常,触发降级", cause);
}
return new StockFeignClient() {
@Override
public StockVO getStock(Long productId) {
StockVO fallback = new StockVO();
fallback.setProductId(productId);
fallback.setQuantity(0);
fallback.setMessage("库存查询暂不可用");
return fallback;
}
@Override
public List<StockVO> getStockBatch(List<Long> productIds) {
// 批量降级:每个商品返回默认库存 0
return productIds.stream()
.map(id -> {
StockVO vo = new StockVO();
vo.setProductId(id);
vo.setQuantity(0);
vo.setMessage("库存查询暂不可用");
return vo;
})
.collect(Collectors.toList());
}
};
}
}
订单服务 Controller(直接调用 Feign,无需 @SentinelResource)
@RestController
@RequestMapping("/order")
@Slf4j
public class OrderController {
@Autowired
private StockFeignClient stockFeignClient;
@GetMapping("/detail/{productId}")
public Result<OrderDetailVO> getOrderDetail(@PathVariable Long productId) {
// Feign 调用已自动受到 Sentinel 保护
// 失败时 FallbackFactory 返回降级结果
StockVO stock = stockFeignClient.getStock(productId);
OrderDetailVO detail = new OrderDetailVO();
detail.setProductId(productId);
detail.setStock(stock);
if (stock.getQuantity() == 0 && "库存查询暂不可用".equals(stock.getMessage())) {
// 降级提示:引导用户稍后重试
detail.setTip("当前库存信息加载失败,显示为默认值,请刷新重试");
}
return Result.success(detail);
}
}
操作前后对比
| 阶段 | Feign 调用结果 | 用户看到的内容 |
|---|---|---|
| 操作前 | 库存服务不可用 → Feign 抛出 RetryableException → 全局异常处理器捕获 → 返回 HTTP 500 | {"code":500,"msg":"系统内部错误"} |
| 操作后 | 库存服务不可用 → SentinelFeign 拦截 → FallbackFactory 捕获异常 → 返回降级 StockVO(quantity=0) | {"code":200,"data":{"productId":100,"quantity":0,"message":"库存查询暂不可用","tip":"当前库存信息加载失败,显示为默认值,请刷新重试"}} |
Feign + Sentinel 拦截原理总结
feign.sentinel.enabled=true后,SentinelFeign自动包装每个FeignClient- 每次 Feign 调用 →
SentinelFeign.invoke()→ 资源名格式GET:http://stock-service/stock/{productId} - 资源自动注册到 Sentinel,Dashboard 可看到并配置规则
- 调用失败(超时/连接拒绝/HTTP 5xx)→
FallbackFactory.create(Throwable)→ 返回降级结果 - 如果需要更细粒度控制,也可以在 Controller 上额外加
@SentinelResource
完整示例 4:规则持久化到 Nacos
场景描述
白歌在某次服务重启后发现 Sentinel Dashboard 上配置的所有熔断规则全部丢失,服务裸奔了 10 分钟后才被发现。大翔 CTO 拍板:必须将 Sentinel 规则持久化到 Nacos,避免重启丢失。
Sentinel Dashboard 规则默认存储在内存中。推模式(Push Mode)下,规则写入 Nacos,应用从 Nacos 读取,Sentinel Dashboard 也可通过 Nacos 同步。
依赖配置(在 Sentinel Starter 基础上,无需额外依赖)
spring:
application:
name: order-service
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080
port: 8719
eager: true
datasource:
# 流控规则数据源
ds-flow:
nacos:
server-addr: 127.0.0.1:8848
namespace: ${NACOS_NAMESPACE:}
group-id: SENTINEL_GROUP
data-id: ${spring.application.name}-flow-rules
data-type: json
rule-type: flow
# 熔断规则数据源
ds-degrade:
nacos:
server-addr: 127.0.0.1:8848
namespace: ${NACOS_NAMESPACE:}
group-id: SENTINEL_GROUP
data-id: ${spring.application.name}-degrade-rules
data-type: json
rule-type: degrade
Nacos 控制台创建熔断规则配置
在 Nacos 控制台中创建配置:
| 配置项 | 值 |
|---|---|
| Data ID | order-service-degrade-rules |
| Group | SENTINEL_GROUP |
| 配置格式 | JSON |
配置内容(JSON 数组):
[
{
"resource": "queryStock",
"grade": 0,
"count": 500,
"timeWindow": 10,
"minRequestAmount": 5,
"statIntervalMs": 10000,
"slowRatioThreshold": 0.5
},
{
"resource": "GET:http://stock-service/stock/{productId}",
"grade": 1,
"count": 0.5,
"timeWindow": 10,
"minRequestAmount": 5,
"statIntervalMs": 10000
}
]
JSON 字段说明:
| JSON 字段 | 含义 | grade=0(慢调用比例) | grade=1(异常比例) | grade=2(异常数) |
|---|---|---|---|---|
resource | 资源名 | — | — | — |
grade | 熔断策略 | 0 | 1 | 2 |
count | 阈值 | 最大 RT(ms) | 异常比例(0~1) | 异常数量 |
timeWindow | 熔断时长(s) | ✓ | ✓ | ✓ |
minRequestAmount | 最小请求数 | ✓ | ✓ | ✓ |
statIntervalMs | 统计窗口(ms) | ✓ | ✓ | ✓ |
slowRatioThreshold | 慢调用比例阈值 | ✓ | — | — |
操作前后对比
| 阶段 | 规则存储位置 | 重启后表现 |
|---|---|---|
| 操作前 | Sentinel Dashboard 内存 | 应用重启后规则全部丢失,需要人工重新配置 |
| 操作后 | Nacos 配置中心 | 应用启动时自动从 Nacos 拉取规则,重启无影响 |
推模式 vs 拉模式
| 模式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 原始模式(内存) | 规则仅存于 Dashboard 内存,应用通过 API 注册 | 无需第三方依赖 | 重启丢失,无高可用 |
| Pull 模式(拉模式) | 应用定时从配置中心拉取规则 | 简单,额外依赖少 | 规则变更有时延,配置中心有轮询压力 |
| Push 模式(推模式) | 配置中心变更后主动推送到应用(Nacos 2.x 基于 gRPC) | 实时性高,规则变更即生效 | 需配置中心支持推送能力 |
推荐:生产环境使用 Push 模式 + Nacos,规则变更实时生效,且重启不丢失。
易错场景
1. 慢调用比例与异常比例的统计口径混淆
小崔在配置慢调用比例规则后,发现库存在超时到 3 秒后全部被 com.netflix.client.ClientException 中断,但熔断器始终未触发。白歌排查后指出:
慢调用比例只看已完成的请求的 RT。如果请求因超时被中断(没有拿到 RT),Sentinel 统计不到它,自然不会计入慢调用。正确的做法是同时配置异常比例熔断(超时也是一种异常)或缩短 Feign 超时时间,确保请求要么在超时前完成(有 RT 可计),要么触发异常(被异常比例统计)。
2. 熔断时间窗口内所有请求一律快速失败
大翔 CTO 在审查黄俪的代码时发现一个认识误区:黄俪以为熔断的 20 秒内,如果库存服务自己恢复了,请求还能"偷偷"调用成功。实际上只要熔断器处于 OPEN 状态,所有请求全部被拦截,直接抛出 DegradeException,不会去尝试调用下游。只有经过 timeWindow 时长后进入 HALF_OPEN 状态,才允许探测请求。这是熔断器的设计初衷——宁可错杀,不可放慢调用拖垮上游。
3. FallbackFactory 与 Fallback 的选择
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 需要记录异常日志 | FallbackFactory | 可以拿到 Throwable,支持按异常类型分类处理和记录 |
| 需要根据异常类型切换不同降级策略 | FallbackFactory | 如连接超时 vs HTTP 503 的降级处理不同 |
| 简单降级,不需要异常信息 | fallback | 代码更简洁,直接返回默认值即可 |
| 需要知道具体哪个 Feign 方法失败 | FallbackFactory | fallback 类需要实现所有方法,FallbackFactory 更灵活 |
4. fallback 和 blockHandler 共存时的调用顺序
这是面试中最常见的陷阱题:
请求进入
│
▼
Slot Chain 检查 (FlowSlot → DegradeSlot)
│
├── 触发 BlockException ──► blockHandler(如果存在)
│ └── 如果 blockHandler 不存在
│ └── fallback(如果存在)
│
└── Slot Chain 通过 ──► 执行方法体
│
├── 业务异常 ──► fallback
└── 正常返回
核心规则:blockHandler 只处理 BlockException,fallback 处理所有 Throwable(包括 BlockException 当 blockHandler 不存在时)。如果两者都存在,先走 blockHandler 处理 BlockException,业务异常走 fallback。
5. 规则持久化时 Data ID 格式必须严格匹配
白歌在配置持久化时踩过一个坑:Nacos 中配置的 Data ID 是 order-service-degrade-rules(带连字符),而 application.yml 中配置的是 ${spring.application.name}-degrade-rules。由于 spring.application.name 的值是 order-service(也带连字符),所以 Data ID 实际是 order-service-degrade-rules,匹配成功。但如果应用名与预期不一致,规则不会生效且无报错日志,排查困难。
检查清单:
- Nacos 控制台中 Data ID 与
application.yml中data-id配置完全一致 group-id与配置一致data-type必须为jsonrule-type必须为degrade(熔断)/flow(流控)
6. Sentinel Dashboard 修改的规则不会自动同步到 Nacos
这是最常见的生产事故:运维在 Sentinel Dashboard 上修改了熔断规则,规则在应用内存中即时生效,但 Nacos 中的 JSON 配置并未更新。应用重启后从 Nacos 读取到旧规则,新规则丢失。
解决方案(二选一):
- 方案 A:直接在 Nacos 控制台管理规则,禁止在 Sentinel Dashboard 修改。Sentinel Dashboard 仅用于监控和查看。
- 方案 B:配置 Sentinel Dashboard 双向同步到 Nacos(需 Dashboard 1.8.4+ 配合自定义
NacosWritableDataSourceRegistry),实现 Dashboard 修改自动回写 Nacos。
面试考点
1. 熔断器三种状态的转换逻辑,HALF_OPEN 为什么是必要的?
状态转换:CLOSED(正常)→ 触发阈值 → OPEN(拒绝所有请求)→ 熔断时长到达 → HALF_OPEN(允许探测)→ 探测成功 → CLOSED / 探测失败 → OPEN。
HALF_OPEN 的必要性:如果没有 HALF_OPEN 状态(即熔断时长结束后直接恢复 CLOSED),一旦下游服务仍未恢复,大量请求会在没有任何缓冲的情况下涌入,再次触发熔断,造成"恢复-熔断-恢复-熔断"的抖动。HALF_OPEN 通过放行少量探测请求来"试探"下游的真实状态,避免了全量恢复带来的二次冲击。
2. Sentinel 熔断与 Hystrix 熔断的核心区别
| 对比维度 | Sentinel | Hystrix |
|---|---|---|
| 熔断策略维度 | 4 种(慢调用比例 + 异常比例 + 异常数 + 分钟异常数) | 1 种(基于命令配置的单一失败率) |
| 慢调用检测 | 支持,按响应时间分桶统计 | 不支持,只看异常不看慢 |
| Dashboard | 独立可视化控制台,规则实时修改 | Dashboard 功能有限,社区版已停更 |
| 隔离机制 | 信号量隔离(无额外线程池开销) | 线程池 + 信号量两种隔离 |
| 规则持久化 | 原生支持推/拉模式持久化 | 不内置 |
| 系统自适应保护 | 支持(Load / CPU / RT / 线程数) | 不支持 |
| HALF_OPEN 探测 | 逐请求探测,成功即恢复 | 半开后需等待一段时间才允许下一次探测 |
| 维护状态 | 阿里巴巴持续活跃维护 | Netflix 已停止维护(进入维护模式) |
3. Feign 集成 Sentinel 的原理:SentinelFeign 如何拦截 Feign 调用?
SentinelFeign 是 Sentinel 为 Feign 提供的适配层,在 feign.sentinel.enabled=true 时自动生效:
SentinelFeign.Builder替换默认的Feign.Builder- 为每个
FeignClient方法创建SentinelInvocationHandler - 每次 Feign 调用 →
SphU.entry(resourceName)进入 Sentinel 责任链 - 资源名格式:
HTTP_METHOD:PROTOCOL://SERVICE_NAME/PATH(如GET:http://stock-service/stock/{productId}) - 调用失败(异常/超时)→ Sentinel 统计异常数/RT → 触发熔断规则 → 抛出
DegradeException FallbackFactory.create(Throwable cause)捕获异常 → 返回降级结果
4. 如何避免熔断后的雪崩恢复不及时?
雪崩恢复不及时的根本原因是熔断后没有合理设置恢复策略:
- 合理设置熔断时长:
timeWindow不宜过长,建议根据下游平均恢复时间设置(通常 10~30 秒)。过长的熔断时长会导致下游已恢复但上游仍拒绝服务。 - HALF_OPEN 探测次数:Sentinel 默认允许少量探测请求进入 HALF_OPEN 状态,探测成功即恢复,探测失败继续熔断。不要自定义限制探测次数,允许 Sentinel 自然探测。
- 配合系统自适应保护:在 SystemSlot 中设置系统级 RT / Load 阈值,防止整体系统被个别慢调用拖垮。
- 监控与告警:通过 Sentinel Dashboard + Prometheus + Grafana 对熔断次数和 Open 状态时长做告警,运维能第一时间介入排查。
5. 降级与限流的区别和联系
| 维度 | 限流(Flow Control) | 熔断降级(Circuit Breaking) |
|---|---|---|
| 目的 | 保护自身不超载 | 保护上游不被慢依赖拖死 |
| 触发条件 | QPS / 并发线程数超阈值 | 下游 RT / 异常比例 / 异常数超阈值 |
| 作用域 | 自身资源 | 调用下游的出口 |
| 恢复方式 | 流量回落后自动恢复 | 熔断时长+半开探测后恢复 |
| 处理方式 | 抛出 FlowException | 抛出 DegradeException |
联系:两者都是 Sentinel 稳定性保障的组成部分,都基于 Slot Chain 责任链(FlowSlot 和 DegradeSlot),都会触发 blockHandler 降级逻辑。在实际生产场景中,通常需要同时配置:流控防自身被打垮,熔断防下游拖垮自身。
6. 规则持久化的两种模式及其适用场景
| 模式 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Pull 模式(拉模式) | 应用定时轮询配置中心,发现有变更则更新规则 | 实现简单,不依赖推送能力 | 规则变更有延迟(取决于轮询间隔),配置中心有轮询压力 | 规则变更不频繁、对实时性要求不高的小规模集群 |
| Push 模式(推模式) | 配置中心变更后主动推送,应用监听并实时更新规则 | 实时性高,规则变更即时生效 | 需要配置中心支持推送能力(如 Nacos 2.x gRPC) | 生产环境、规则频繁变更、对实时性要求高的场景 |
Spring Cloud Alibaba 2025.1.x 推荐:使用 Nacos 2.x + Push 模式,gRPC 双向流实现规则实时推送,变更延迟 < 500ms。
本章总结
| 知识模块 | 核心要点 |
|---|---|
| 熔断策略 | 四种策略各有适用场景:慢调用比例适合性能劣化检测,异常比例/异常数适合可用性检测 |
| 状态机 | CLOSED → OPEN → HALF_OPEN → CLOSED 三态转换,HALF_OPEN 是防止恢复抖动的关键 |
| Feign 集成 | feign.sentinel.enabled=true 后自动拦截,推荐 FallbackFactory 获取异常信息 |
| 降级方法 | blockHandler 处理 BlockException,fallback 处理业务异常,共存时走不同分支 |
| 规则持久化 | 推荐 Push 模式持久化到 Nacos,生产环境避免依赖 Dashboard 内存存储 |
| 与 Hystrix 对比 | Sentinel 策略更多、维护更活跃、Dashboard 更强、无额外线程池开销 |
下一章预告:06_Seata 分布式事务 — 飞翔科技电商平台迎来双十一,订单、库存、账户三服务跨库写入,大翔 CTO 带领团队用 Seata AT 模式实现零侵入分布式事务。