乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • Spring Cloud Alibaba概述与技术选型
  • Nacos注册中心
  • Nacos配置中心
  • Sentinel流量控制
  • Sentinel降级与熔断
  • Seata分布式事务
  • RocketMQ消息驱动
  • Spring AI Alibaba与AI集成
  • Dubbo RPC服务调用
  • Gateway服务网关
  • GraalVM静态编译
  • 其他组件速览
  • Alibaba最佳实践与面试考点

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 提供两种降级处理方法,层次分明:

方法处理范围参数特征使用场景
blockHandlerBlockException(流控/熔断/系统保护等触发)参数与原方法一致 + 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 熔断

维度SentinelHystrix
熔断策略慢调用比例 + 异常比例 + 异常数 + 分钟异常数(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
最大 RT500ms响应时间超过 500ms 算慢调用
比例阈值0.550% 的请求是慢调用即触发熔断
熔断时长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 5sSentinel 检测 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 拦截原理总结

  1. feign.sentinel.enabled=true 后,SentinelFeign 自动包装每个 FeignClient
  2. 每次 Feign 调用 → SentinelFeign.invoke() → 资源名格式 GET:http://stock-service/stock/{productId}
  3. 资源自动注册到 Sentinel,Dashboard 可看到并配置规则
  4. 调用失败(超时/连接拒绝/HTTP 5xx)→ FallbackFactory.create(Throwable) → 返回降级结果
  5. 如果需要更细粒度控制,也可以在 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 IDorder-service-degrade-rules
GroupSENTINEL_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熔断策略012
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 方法失败FallbackFactoryfallback 类需要实现所有方法,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 必须为 json
  • rule-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 熔断的核心区别

对比维度SentinelHystrix
熔断策略维度4 种(慢调用比例 + 异常比例 + 异常数 + 分钟异常数)1 种(基于命令配置的单一失败率)
慢调用检测支持,按响应时间分桶统计不支持,只看异常不看慢
Dashboard独立可视化控制台,规则实时修改Dashboard 功能有限,社区版已停更
隔离机制信号量隔离(无额外线程池开销)线程池 + 信号量两种隔离
规则持久化原生支持推/拉模式持久化不内置
系统自适应保护支持(Load / CPU / RT / 线程数)不支持
HALF_OPEN 探测逐请求探测,成功即恢复半开后需等待一段时间才允许下一次探测
维护状态阿里巴巴持续活跃维护Netflix 已停止维护(进入维护模式)

3. Feign 集成 Sentinel 的原理:SentinelFeign 如何拦截 Feign 调用?

SentinelFeign 是 Sentinel 为 Feign 提供的适配层,在 feign.sentinel.enabled=true 时自动生效:

  1. SentinelFeign.Builder 替换默认的 Feign.Builder
  2. 为每个 FeignClient 方法创建 SentinelInvocationHandler
  3. 每次 Feign 调用 → SphU.entry(resourceName) 进入 Sentinel 责任链
  4. 资源名格式:HTTP_METHOD:PROTOCOL://SERVICE_NAME/PATH(如 GET:http://stock-service/stock/{productId})
  5. 调用失败(异常/超时)→ Sentinel 统计异常数/RT → 触发熔断规则 → 抛出 DegradeException
  6. 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 模式实现零侵入分布式事务。

上一页
Sentinel流量控制
下一页
Seata分布式事务