【架构实战】服务熔断与限流Sentinel:高可用服务的守护神

一、一次"雪崩"让我彻底清醒

凌晨三点,刺耳的报警声把我从睡梦中惊醒。

监控系统显示:订单服务RT飙升,99线延迟超过5秒。紧接着,支付服务、库存服务、物流服务......一个接一个地沦陷,整个系统陷入了瘫痪。

我赶紧爬起来排查。发现起因很简单:某个上游服务突然变慢,导致调用它的服务线程池被占满,然后拖垮了更多服务,最终引发了连锁反应------这就是传说中的"雪崩效应"。

那个夜晚,我一边手动重启服务,一边发誓:再也不让这种事发生。

后来,我接触到了Sentinel。这个阿里开源的流量控制组件,彻底改变了我的系统防护思路。今天就把我的经验分享出来,希望你别再踩我踩过的坑。


二、熔断与限流:傻傻分不清?

很多人把熔断和限流混为一谈,其实它们解决的问题完全不同。

2.1 限流:保护系统不被"挤爆"

复制代码
场景:每秒1万个请求,但服务只能处理1000个
问题:如果不限制,全部1万个请求都会打到服务上,导致服务崩溃

限流方案:只放行1000个请求,其余9000个拒绝或排队

限流的核心是保护"系统本身",让服务不被瞬时流量打垮。

2.2 熔断:保护系统不被"慢服务"拖垮

复制代码
场景:服务A依赖服务B,服务B突然变慢
问题:如果持续调用变慢的服务B,服务A的线程池会被耗尽,最终服务A也变慢

熔断方案:检测到服务B持续变慢时,"跳闸"------暂时不调用服务B,直接返回降级结果

熔断的核心是"隔离故障",防止故障蔓延。

2.3 降级:给用户一个"Plan B"

java 复制代码
// 没有降级
public OrderResult createOrder(OrderDTO order) {
    // 调用库存服务
    InventoryResponse inventory = inventoryService.check(order.getSkuId());
    // ...
}

// 有降级
public OrderResult createOrder(OrderDTO order) {
    try {
        InventoryResponse inventory = inventoryService.check(order.getSkuId());
    } catch (Exception e) {
        // 降级:允许无库存下单,后续处理
        log.warn("库存服务不可用,使用降级策略");
        InventoryResponse fallback = new InventoryResponse();
        fallback.setAvailable(false);
        fallback.setMessage("库存服务降级,稍后确认");
        // 继续处理订单
    }
}

降级的核心是"有备无患",当服务不可用时,给出一个兜底方案。


三、Sentinel核心概念

3.1 三大核心组件

复制代码
Sentinel核心架构:

┌─────────────────────────────────────────────────┐
│                   Sentinel Core                  │
│                                                   │
│  ┌─────────────┐  ┌─────────────┐  ┌──────────┐  │
│  │  限流规则    │  │  熔断规则    │  │  热点规则 │  │
│  │ (FlowRule)  │  │(DegradeRule)│  │(ParamFlow)│  │
│  └─────────────┘  └─────────────┘  └──────────┘  │
│                                                   │
│  ┌─────────────────────────────────────────────┐ │
│  │            Slot Chain(插槽链)              │ │
│  │  ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐      │ │
│  │  │Node│→│Flow│→│Degr│→│Sys │→│Auth│      │ │
│  │  │统计│ │限流│ │熔断│ │系统│ │授权│      │ │
│  │  └────┘ └────┘ └────┘ └────┘ └────┘      │ │
│  └─────────────────────────────────────────────┘ │
│                                                   │
└─────────────────────────────────────────────────┘

3.2 限流算法:四种模式

java 复制代码
// 1. 直接拒绝模式(直接拒绝超出阈值的请求)
FlowRule rule = new FlowRule();
rule.setResource("orderService");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);  // 按QPS限流
rule.setCount(100);  // 每秒最多100个请求
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
sentinelRuleManager.save(rule);

// 2. 冷启动模式(渐进式增加流量,防止冷启动冲击)
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
// 冷启动持续时间:10秒,从0逐步增加到100

// 3. 匀速排队模式(让请求匀速通过,防止突刺)
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
rule.setMaxQueueingTimeMs(500);  // 最大排队时间500ms

// 4. 冷启动+匀速排队
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER);

3.3 熔断策略:三种模式

java 复制代码
// 1. 慢调用比例熔断
DegradeRule rule = new DegradeRule();
rule.setResource("orderService");
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);  // 按响应时间
rule.setCount(2000);  // 超过2000ms视为慢调用
rule.setSlowRatioThreshold(0.5);  // 慢调用比例阈值50%
rule.setMinRequestAmount(5);  // 最小请求数5个
rule.setStatIntervalMs(10000);  // 10秒内
// 触发条件:10秒内至少5个请求,且50%以上超过2秒
// 熔断时长:10秒

// 2. 异常比例熔断
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
rule.setCount(0.5);  // 异常比例50%
rule.setMinRequestAmount(5);
// 触发条件:5个请求中,50%以上异常

// 3. 异常数熔断
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
rule.setCount(10);  // 异常数达到10个
rule.setMinRequestAmount(5);

四、Sentinel实战:Spring Boot集成

4.1 引入依赖

xml 复制代码
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.6</version>
</dependency>

<!-- Spring Boot适配 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-boot-starter</artifactId>
    <version>1.8.6</version>
</dependency>

<!-- 注解支持 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-annotation-aspectj</artifactId>
    <version>1.8.6</version>
</dependency>

4.2 配置Sentinel

yaml 复制代码
# application.yml
spring:
  cloud:
    sentinel:
      enabled: true
      transport:
        dashboard: localhost:8080  # Sentinel控制台地址
        port: 8719  # 客户端端口
      eager: true  # 启动时加载规则

4.3 使用Sentinel注解

java 复制代码
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * 使用Sentinel资源保护
     * resource: 资源名(唯一标识)
     * blockHandler: 限流/熔断时的处理方法
     * fallback: 异常时的降级方法
     */
    @GetMapping("/create")
    @SentinelResource(value = "orderCreate",
                      blockHandler = "orderBlockHandler",
                      fallback = "orderFallback")
    public Result<OrderDTO> createOrder(@RequestParam String userId,
                                         @RequestParam String skuId) {
        return orderService.createOrder(userId, skuId);
    }

    /**
     * 限流/熔断时的处理
     */
    public Result<OrderDTO> orderBlockHandler(String userId, String skuId,
                                               BlockException e) {
        log.warn("订单服务被限流/熔断: {}", e.getClass().getSimpleName());
        return Result.fail("系统繁忙,请稍后再试");
    }

    /**
     * 异常降级
     */
    public Result<OrderDTO> orderFallback(String userId, String skuId,
                                          Throwable e) {
        log.error("订单服务异常: userId={}", userId, e);
        return Result.fail("服务异常,请稍后再试");
    }
}

五、限流规则实战配置

5.1 QPS限流

java 复制代码
// 按QPS限流,每秒最多100个请求
private static void initFlowRule() {
    List<FlowRule> rules = new ArrayList<>();

    FlowRule rule = new FlowRule();
    rule.setResource("orderCreate");
    rule.setCount(100);  // QPS阈值
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);  // 按QPS
    rule.setLimitApp("default");  // 默认为所有应用

    // 直接拒绝模式
    rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);

    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}

5.2 并发线程数限流

java 复制代码
// 按并发线程数限流
private static void initThreadFlowRule() {
    List<FlowRule> rules = new ArrayList<>();

    FlowRule rule = new FlowRule();
    rule.setResource("orderCreate");
    rule.setCount(10);  // 最多10个并发线程
    rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);  // 按线程数
    rules.add(rule);

    FlowRuleManager.loadRules(rules);
}

5.3 基于调用关系的限流

java 复制代码
// 调用关系限流:从属资源限流
// 当 orderService 调用 inventoryService 时,对 orderService→inventoryService 这条链路限流
private static void initRelationFlowRule() {
    List<FlowRule> rules = new ArrayList<>();

    // 入口流量限流
    FlowRule entryRule = new FlowRule();
    entryRule.setResource("orderCreate");
    entryRule.setCount(100);
    entryRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    entryRule.setStrategy(RuleConstant.STRATEGY_DIRECT);  // 直接限流
    rules.add(entryRule);

    // 基于链路的限流
    FlowRule chainRule = new FlowRule();
    chainRule.setResource("inventoryService");
    chainRule.setCount(50);
    chainRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    chainRule.setStrategy(RuleConstant.STRATEGY_CHAIN);  // 链路限流
    chainRule.setLimitApp("orderService");  // orderService调用inventoryService时生效
    rules.add(chainRule);

    FlowRuleManager.loadRules(rules);
}

六、熔断规则实战配置

6.1 慢调用比例熔断

java 复制代码
// 配置慢调用比例熔断
private static void initDegradeRule() {
    List<DegradeRule> rules = new ArrayList<>();

    DegradeRule rule = new DegradeRule();
    rule.setResource("orderService");
    rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);  // 按RT熔断
    rule.setCount(1000);  // RT阈值1000ms
    rule.setSlowRatioThreshold(0.5);  // 50%慢调用比例触发熔断
    rule.setMinRequestAmount(5);  // 最小请求数
    rule.setTimeWindow(10);  // 熔断时长10秒

    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}

6.2 异常比例熔断

java 复制代码
// 配置异常比例熔断
private static void initExceptionDegradeRule() {
    List<DegradeRule> rules = new ArrayList<>();

    DegradeRule rule = new DegradeRule();
    rule.setResource("orderService");
    rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
    rule.setCount(0.5);  // 50%异常比例
    rule.setMinRequestAmount(5);  // 最小请求数
    rule.setTimeWindow(10);  // 熔断时长10秒

    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}

6.3 熔断状态机

复制代码
熔断器状态转换:

            ┌──────────┐
            │  CLOSED  │  正常状态
            │ (关闭)   │  所有请求正常通过
            └────┬─────┘
                 │
                 │ 触发熔断条件
                 │ (慢调用≥50% 或 异常≥50%)
                 ↓
            ┌──────────┐
            │  OPEN    │  熔断状态
            │ (打开)   │  所有请求被拒绝/降级
            └────┬─────┘
                 │
                 │ 熔断时间到达
                 │ (等待10秒)
                 ↓
            ┌──────────┐
            │ HALF_OPEN│ 半开状态
            │ (半开)   │  允许一个请求通过
            └────┬─────┘
                 │
        ┌────────┴────────┐
        ↓                 ↓
     请求成功           请求失败
        ↓                 ↓
   ┌────────┐        ┌────────┐
   │CLOSED  │        │ OPEN   │
   │恢复    │        │继续熔断│
   └────────┘        └────────┘

七、热点规则:参数限流

7.1 热点参数限流

java 复制代码
// 热点规则:对特定参数值限流
private static void initParamFlowRule() {
    List<ParamFlowRule> rules = new ArrayList<>();

    ParamFlowRule rule = new ParamFlowRule("orderQuery")
        // 参数索引:0表示第一个参数(userId)
        .setParamIdx(0)
        .setCount(10);  // 同一个userId,每秒最多10次

    // 针对特定参数值的限流
    ParamFlowItem item = new ParamFlowItem()
        .setObject(String.valueOf("vip_user_001"))  // VIP用户
        .setClassType(String.class.getName())
        .setCount(100);  // VIP用户放宽到每秒100次

    rule.setParamFlowItemList(Collections.singletonList(item));

    rules.add(rule);
    ParamFlowRuleManager.loadRules(rules);
}

7.2 代码中使用热点限流

java 复制代码
@RestController
@RequestMapping("/product")
public class ProductController {

    @GetMapping("/detail")
    @SentinelResource(value = "productDetail",
                      blockHandler = "productBlockHandler")
    public Result<Product> getProductDetail(
            @RequestParam String productId,
            @RequestParam(required = false) String userId) {
        return productService.getProductDetail(productId);
    }

    public Result<Product> productBlockHandler(String productId, String userId,
                                               BlockException e) {
        return Result.fail("访问太频繁,请稍后再试");
    }
}

八、Sentinel Dashboard配置

8.1 启动Dashboard

bash 复制代码
# 下载 Sentinel Dashboard
wget https://github.com/alibaba/Sentinel/releases/download/1.8.6/sentinel-dashboard-1.8.6.jar

# 启动(用户名/密码都是 sentinel)
java -Dserver.port=8080 \
     -Dcsp.sentinel.dashboard.server=localhost:8080 \
     -Dproject.name=sentinel-dashboard \
     -jar sentinel-dashboard-1.8.6.jar

8.2 Dashboard配置限流规则

java 复制代码
// 代码中定义资源
@SentinelResource(value = "orderCreate")

// Dashboard中配置规则(不需要重启应用)
// 1. 打开 http://localhost:8080
// 2. 登录(sentinel/sentinel)
// 3. 点击"流控规则" → "新增流控规则"
// 4. 填写:
//    - 资源名:orderCreate
//    - 阈值类型:QPS
//    - 单机阈值:100
//    - 流控模式:直接
//    - 流控效果:快速失败
// 5. 点击"新增"

九、实战案例:订单系统限流保护

9.1 场景分析

复制代码
订单系统架构:

用户请求
    ↓
┌─────────────────────────────────────┐
│            限流层(Gateway)          │
│  - 每秒最多10000请求                  │
│  - 超出返回429 Too Many Requests     │
└─────────────────────────────────────┘
    ↓
┌─────────────────────────────────────┐
│            订单服务                   │
│  - 每用户限流:每秒10次                │
│  - 每接口限流:每秒1000次              │
│  - 熔断:支付服务响应>2秒时触发        │
└─────────────────────────────────────┘
    ↓
┌───────────┬───────────┬───────────┐
│ 库存服务  │ 支付服务  │ 用户服务  │
│ (本地)   │ (RPC)    │ (RPC)    │
└───────────┴───────────┴───────────┘

9.2 完整配置

java 复制代码
@Configuration
public class SentinelConfig {

    @PostConstruct
    public void init() {
        // 初始化限流规则
        initFlowRules();

        // 初始化熔断规则
        initDegradeRules();
    }

    /**
     * 限流规则初始化
     */
    private void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();

        // 1. 订单创建接口限流:每秒100次
        FlowRule createRule = new FlowRule();
        createRule.setResource("orderCreate");
        createRule.setCount(100);
        createRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        createRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        rules.add(createRule);

        // 2. 订单查询接口限流:每秒500次
        FlowRule queryRule = new FlowRule();
        queryRule.setResource("orderQuery");
        queryRule.setCount(500);
        queryRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rules.add(queryRule);

        // 3. 热点规则:热门商品查询每秒10次
        ParamFlowRule hotRule = new ParamFlowRule("productDetail")
            .setParamIdx(0)  // productId参数
            .setCount(10)
            .setDurationInSec(1);
        rules.add(hotRule);

        FlowRuleManager.loadRules(rules);
    }

    /**
     * 熔断规则初始化
     */
    private void initDegradeRules() {
        List<DegradeRule> rules = new ArrayList<>();

        // 1. 支付服务熔断:RT>2秒且比例>50%时触发
        DegradeRule paymentRule = new DegradeRule();
        paymentRule.setResource("paymentService");
        paymentRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        paymentRule.setCount(2000);  // 2秒
        paymentRule.setSlowRatioThreshold(0.5);
        paymentRule.setMinRequestAmount(5);
        paymentRule.setTimeWindow(10);  // 熔断10秒
        rules.add(paymentRule);

        // 2. 库存服务熔断:异常比例>30%时触发
        DegradeRule inventoryRule = new DegradeRule();
        inventoryRule.setResource("inventoryService");
        inventoryRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
        inventoryRule.setCount(0.3);  // 30%
        inventoryRule.setMinRequestAmount(5);
        inventoryRule.setTimeWindow(30);
        rules.add(inventoryRule);

        DegradeRuleManager.loadRules(rules);
    }
}

十、踩坑实录

坑1:Sentinel规则没有持久化

项目上线后,在Dashboard上配置了限流规则。重启服务器后,规则全部丢失!

原因:Sentinel默认把规则存在内存中,重启后会丢失。

解决:使用动态规则推送,把规则存到Nacos、Apollo等配置中心,或者用Sentinel的Redis适配器。

java 复制代码
// 使用Nacos动态推送规则
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.8.6</version>
</dependency>

// 配置Nacos数据源
spring:
  cloud:
    sentinel:
      datasource:
        ds:
          nacos:
            server-addr: localhost:8848
            data-id: ${spring.application.name}-flow-rules
            group-id: SENTINEL_GROUP
            rule-type: flow

坑2:Sentinel注解在私有方法上不生效

我把@SentinelResource加到了私有方法上,结果限流不生效。

原因@SentinelResource是基于AOP的,Spring AOP不会拦截同类内部调用。

解决 :把方法移到另一个Service类中,或者使用SphU.entry()手动埋点。

java 复制代码
// 错误:内部调用,注解不生效
@Service
public class OrderService {
    public void create() {
        this.query();  // 内部调用,@SentinelResource不生效
    }

    @SentinelResource(value = "query", blockHandler = "block")
    public void query() { }
}

// 正确:使用SphU手动埋点
@Service
public class OrderService {
    public void create() {
        try {
            Entry entry = SphU.entry("query");
            query();
            entry.exit();
        } catch (BlockException e) {
            // 限流处理
        }
    }
}

坑3:熔断阈值设置过大

熔断阈值设置为5000ms(5秒),但实际上1秒以上的延迟就已经很危险了。

教训:熔断阈值要设置得比业务超时时间短,留出缓冲时间。建议设置为业务超时的50%-80%。

坑4:没有配置限流后的降级策略

限流后的请求直接报错,用户体验很差。

解决:配置友好的限流提示,或者跳转到排队页面。

java 复制代码
// 自定义限流处理
public Result orderBlockHandler(String userId, String skuId, BlockException e) {
    if (e instanceof FlowException) {
        return Result.fail(429, "请求太频繁,请稍后再试");
    }
    if (e instanceof DegradeException) {
        return Result.fail(503, "服务暂时不可用,请稍后再试");
    }
    return Result.fail("系统繁忙");
}

十一、总结

Sentinel是保护系统高可用的重要工具:

  • 限流:保护系统不被瞬时流量打垮
  • 熔断:防止故障蔓延,避免雪崩
  • 降级:提供兜底方案,保证核心功能可用
  • 热点参数限流:针对特定参数值的精细化限流

最佳实践:

  1. 限流阈值要合理,通过压测确定
  2. 熔断阈值要小于业务超时时间
  3. 限流/熔断后要给用户友好的提示
  4. 规则要持久化,不要只在内存中
  5. 监控要做好,能及时发现问题

血的教训:

不要以为上了限流和熔断就高枕无忧了。如果阈值设置不合理,该限的时候不限,该熔断的时候不熔断,问题照样会发生。建议在上线前做充分的压测和演练。

思考题: 你的系统目前有限流和熔断吗?阈值是怎么确定的?有没有遇到过"该限不限"的问题?


个人观点,仅供参考

相关推荐
上海云盾第一敬业销售1 小时前
选择适合企业的高防CDN服务:架构解析与实践分享
安全·web安全·架构
momom2 小时前
分布式缓存集群高可用架构与一致性哈希优化实践
分布式·后端·架构
hhhhhaaa2 小时前
多节点矩阵式任务系统:统一配置中心与动态规则引擎架构设计
后端·算法·架构
heimeiyingwang2 小时前
【架构实战】分布式事务TCC模式:两阶段提交的工程艺术
分布式·架构
大江东去浪淘尽千古风流人物2 小时前
【Kimera】MIT SPARK 实时度量-语义 SLAM 全栈解析:VIO + 鲁棒 PGO + 语义网格四模块架构与 EuRoC 实测深度剖析
大数据·架构·spark
大江东去浪淘尽千古风流人物2 小时前
【Kimera-VIO】MIT SPARK 实时度量-语义 VIO/SLAM:六模块并行架构与智能因子图优化深度解析
大数据·架构·spark
大江东去浪淘尽千古风流人物3 小时前
【Kimera-Semantics】实时三维语义重建深度解析:Fast/Merged 双路积分、对数概率体素 Bayesian 融合与 ROS 全链路实现
大数据·架构·spark
@不误正业3 小时前
多Agent协作框架深度实战-从ReAct到Plan-and-Execute全架构演进
前端·react.js·架构·agent
小谢小哥3 小时前
59-消息推送系统详解
java·后端·架构