引言:当整个系统都在危险边缘

在前面的文章中,我们学习了限流熔断,它们都是针对单个资源的保护:

  • 限流:保护单个接口不被流量压垮
  • 熔断:保护自己不被依赖服务拖垮

但在实际生产中,还有一种更危险的场景:

某天晚上8点,你的电商系统突然收到一波超大流量(可能是爬虫、攻击或者真实用户)。

虽然你对每个接口都做了限流,但这波流量分散在各个接口上:

  • 首页接口:QPS 500(限流阈值600)✅
  • 搜索接口:QPS 300(限流阈值400)✅
  • 商品详情:QPS 400(限流阈值500)✅
  • 下单接口:QPS 200(限流阈值300)✅

每个接口都没有超过限流阈值,但整个系统的负载已经爆炸了

  • CPU使用率:95%
  • 系统Load:8.0(4核机器)
  • 平均响应时间:5秒
  • 内存使用率:90%

系统已经濒临崩溃边缘!

这就是单个资源限流的盲区:无法感知系统整体的健康状况。

Sentinel的系统自适应保护(System Adaptive Protection)就是为了解决这个问题。


系统保护的必要性

为什么需要系统级别的保护?

问题1:单个资源限流无法保护整体

┌─────────────────────────────────────┐
│          服务器(4核8G)              │
├─────────────────────────────────────┤
│ 接口A:QPS 500/600 ✅ (83%)         │
│ 接口B:QPS 300/400 ✅ (75%)         │
│ 接口C:QPS 400/500 ✅ (80%)         │
│ 接口D:QPS 200/300 ✅ (67%)         │
├─────────────────────────────────────┤
│ 但是...                             │
│ CPU使用率:95% ❌                    │
│ Load:8.0 ❌ (4核机器)              │
│ 平均RT:5秒 ❌                       │
└─────────────────────────────────────┘

问题2:流量来源不均匀

  • 不同接口的计算复杂度不同
  • 不同接口的数据库查询次数不同
  • 突发流量可能来自多个接口

问题3:无法应对突发故障

  • 数据库突然变慢
  • 缓存突然失效
  • GC突然频繁

系统保护的目标

在系统整体负载过高时,从入口处开始限流,保证系统不被压垮。

核心思想

  1. 监控系统指标:Load、CPU、RT、线程数等
  2. 智能判断:系统是否处于危险状态
  3. 全局限流:从入口处限流,保护整个系统

五大系统保护指标

Sentinel提供了五个维度的系统保护指标:

1. Load(系统负载)

定义:Linux的系统平均负载(Load Average)。

含义

  • Load = 1.0:1核CPU满载
  • Load = 4.0:4核CPU满载
  • Load = 8.0:4核CPU严重过载(2倍负载)

触发条件

当前Load >= 阈值 && 当前并发线程数 >= 系统容量(BBR算法计算)

适用场景

  • Linux系统(Windows不支持Load指标)
  • 希望根据整体负载保护系统

配置示例

SystemRule rule = new SystemRule();
rule.setHighestSystemLoad(4.0); // Load阈值:4.0

解读:当系统Load >= 4.0,且系统容量已满,触发限流。

2. CPU使用率

定义:当前系统的CPU使用率(0.0 ~ 1.0)。

触发条件

当前CPU使用率 >= 阈值

适用场景

  • 跨平台(Linux、Windows都支持)
  • CPU密集型应用

配置示例

SystemRule rule = new SystemRule();
rule.setHighestCpuUsage(0.8); // CPU使用率阈值:80%

解读:当CPU使用率 >= 80%,触发限流。

3. 平均响应时间(RT)

定义:所有入口流量的平均响应时间。

触发条件

当前平均RT >= 阈值

适用场景

  • 关注响应时间的系统
  • 防止雪崩效应

配置示例

SystemRule rule = new SystemRule();
rule.setAvgRt(1000); // 平均RT阈值:1000ms

解读:当平均响应时间 >= 1秒,触发限流。

4. 并发线程数

定义:系统当前正在处理的并发线程数。

触发条件

当前并发线程数 >= 阈值

适用场景

  • 防止线程池耗尽
  • 保护下游依赖

配置示例

SystemRule rule = new SystemRule();
rule.setMaxThread(100); // 并发线程数阈值:100

解读:当并发线程数 >= 100,触发限流。

5. 入口QPS

定义:系统所有入口流量的总QPS。

触发条件

当前入口QPS >= 阈值

适用场景

  • 已知系统整体吞吐能力
  • 简单粗暴的全局限流

配置示例

SystemRule rule = new SystemRule();
rule.setQps(10000); // 入口QPS阈值:10000

解读:当入口QPS >= 10000,触发限流。


自适应算法:BBR

什么是BBR?

BBR(Bottleneck Bandwidth and Round-trip propagation time)是Google提出的拥塞控制算法,Sentinel借鉴了这个思想用于系统保护。

核心思想

根据系统的处理能力(Capacity)动态调整限流阈值,让系统始终运行在最佳状态。

BBR算法原理

关键指标

  1. maxQps:系统处理的最大QPS(通过滑动窗口统计)
  2. minRt:系统处理的最小响应时间(通过滑动窗口统计)
  3. 系统容量(Capacity)maxQps * minRt / 1000

触发条件(以Load为例):

if (currentLoad >= threshold) {
    // 计算系统容量
    capacity = maxQps * minRt / 1000;

    // 如果当前并发线程数 >= 容量,触发限流
    if (currentThread >= capacity) {
        return BLOCK;
    }
}

举例

  • maxQps = 1000(系统在最佳状态下能处理1000 QPS)
  • minRt = 10ms(系统在最佳状态下的响应时间)
  • capacity = 1000 * 10 / 1000 = 10(系统容量:10个并发线程)

当Load >= 阈值时

  • 如果当前并发线程数 < 10,放行
  • 如果当前并发线程数 >= 10,限流

为什么需要BBR?

问题:如果只看Load,可能会误判。

场景1:Load高但系统正常

  • Load = 5.0(高)
  • 但是系统只有2个并发请求(低)
  • 不应该限流(可能是其他进程导致的Load高)

场景2:Load高且系统过载

  • Load = 5.0(高)
  • 系统有50个并发请求(高)
  • 系统容量只有10
  • 应该限流

BBR算法通过结合Load并发线程数,避免了误判。


配置系统保护规则

基本配置

import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;

import java.util.ArrayList;
import java.util.List;

public class SystemRuleConfig {

    public static void initSystemRule() {
        List<SystemRule> rules = new ArrayList<>();

        SystemRule rule = new SystemRule();

        // 方式1:Load保护(Linux)
        rule.setHighestSystemLoad(4.0);

        // 方式2:CPU使用率保护(跨平台)
        // rule.setHighestCpuUsage(0.8);

        // 方式3:平均RT保护
        // rule.setAvgRt(1000);

        // 方式4:并发线程数保护
        // rule.setMaxThread(100);

        // 方式5:入口QPS保护
        // rule.setQps(10000);

        rules.add(rule);
        SystemRuleManager.loadRules(rules);
        System.out.println("✅ 系统保护规则已加载");
    }
}

注意

  • 系统保护规则是全局的,只需配置一条
  • 五个指标可以同时配置,任意一个触发都会限流
  • 但通常只配置1-2个最关键的指标

Dashboard配置

在Sentinel Dashboard中:

  1. 选择应用
  2. 点击"系统规则"
  3. 点击"新增系统规则"
  4. 配置阈值

实战案例:保护电商系统

场景描述

电商系统部署在4核8G的服务器上,正常情况下:

  • CPU使用率:50%
  • Load:2.0
  • 平均RT:100ms
  • 并发线程数:20

某天晚上遭遇流量攻击,QPS从1000飙升到5000,系统濒临崩溃。

配置策略

import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;

import java.util.ArrayList;
import java.util.List;

public class EcommerceSystemProtection {

    public static void main(String[] args) throws InterruptedException {
        // 1. 配置系统保护规则
        initSystemRule();

        // 2. 模拟正常流量
        System.out.println("=== 正常流量 ===");
        for (int i = 0; i < 10; i++) {
            simulateRequest();
            Thread.sleep(100);
        }

        // 3. 模拟系统过载
        System.out.println("\n=== 系统过载(模拟CPU 90%)===");
        SystemRuleManager.loadRules(createHighLoadRules());

        for (int i = 0; i < 10; i++) {
            simulateRequest();
            Thread.sleep(100);
        }
    }

    /**
     * 配置系统保护规则
     */
    private static void initSystemRule() {
        List<SystemRule> rules = new ArrayList<>();

        SystemRule rule = new SystemRule();
        // CPU使用率保护:80%
        rule.setHighestCpuUsage(0.8);

        // 平均RT保护:500ms
        rule.setAvgRt(500);

        // 并发线程数保护:50
        rule.setMaxThread(50);

        rules.add(rule);
        SystemRuleManager.loadRules(rules);
        System.out.println("✅ 系统保护规则已加载:CPU < 80%, RT < 500ms, 线程数 < 50\n");
    }

    /**
     * 创建高负载规则(用于模拟)
     */
    private static List<SystemRule> createHighLoadRules() {
        List<SystemRule> rules = new ArrayList<>();
        SystemRule rule = new SystemRule();
        rule.setHighestCpuUsage(0.5); // 降低阈值,模拟触发
        rules.add(rule);
        return rules;
    }

    /**
     * 模拟请求
     */
    private static void simulateRequest() {
        try {
            // 注意:系统保护不需要定义资源,它会自动保护所有入口流量
            // 这里只是模拟业务逻辑
            System.out.println("✅ 请求处理成功");
        } catch (Exception e) {
            System.out.println("🔴 请求被系统保护拒绝");
        }
    }
}

效果分析

正常情况(CPU 50%,RT 100ms):

✅ 请求处理成功
✅ 请求处理成功
✅ 请求处理成功

系统过载(CPU 90%,RT 500ms):

🔴 请求被系统保护拒绝
🔴 请求被系统保护拒绝
✅ 请求处理成功(少量放行)
🔴 请求被系统保护拒绝

收益

  • 系统不会崩溃
  • CPU使用率被控制在安全范围
  • 少量请求仍然能够成功(保证基本可用)

系统保护 vs 限流 vs 熔断

三者对比

维度系统保护限流熔断
保护对象整个系统单个资源调用依赖
触发条件Load、CPU、RT等QPS、线程数慢调用、异常
保护范围全局入口指定资源指定依赖
粒度粗(系统级)中(资源级)细(调用级)
适用场景整体过载接口热点依赖故障

三层防护体系

           用户请求
               ↓
    ┌──────────────────┐
    │   系统保护        │  ← 第一层:整体负载保护
    │  (Load/CPU/RT)   │
    └──────────────────┘
               ↓
    ┌──────────────────┐
    │   接口限流        │  ← 第二层:接口级保护
    │  (QPS/线程数)     │
    └──────────────────┘
               ↓
    ┌──────────────────┐
    │   依赖熔断        │  ← 第三层:依赖保护
    │  (慢调用/异常)    │
    └──────────────────┘
               ↓
           业务处理

实战组合使用

public class ComprehensiveProtection {

    public static void init() {
        // 第一层:系统保护
        List<SystemRule> systemRules = new ArrayList<>();
        SystemRule systemRule = new SystemRule();
        systemRule.setHighestCpuUsage(0.8); // CPU 80%
        systemRule.setAvgRt(500); // RT 500ms
        systemRules.add(systemRule);
        SystemRuleManager.loadRules(systemRules);

        // 第二层:接口限流
        List<FlowRule> flowRules = new ArrayList<>();
        FlowRule flowRule = new FlowRule();
        flowRule.setResource("orderCreate");
        flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        flowRule.setCount(100); // QPS 100
        flowRules.add(flowRule);
        FlowRuleManager.loadRules(flowRules);

        // 第三层:依赖熔断
        List<DegradeRule> degradeRules = new ArrayList<>();
        DegradeRule degradeRule = new DegradeRule();
        degradeRule.setResource("callInventoryService");
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        degradeRule.setCount(1000); // RT 1秒
        degradeRule.setSlowRatioThreshold(0.5);
        degradeRules.add(degradeRule);
        DegradeRuleManager.loadRules(degradeRules);

        System.out.println("✅ 三层防护体系已建立");
    }
}

最佳实践

1. 选择合适的指标

场景推荐指标
Linux服务器Load + CPU
Windows服务器CPU + RT
计算密集型CPU
IO密集型Load + RT
响应时间敏感RT
已知吞吐能力入口QPS

2. 阈值设置建议

CPU使用率

  • 推荐:0.8(80%)
  • 激进:0.7(70%)
  • 保守:0.9(90%)

Load(以4核服务器为例):

  • 推荐:3.0(75%负载)
  • 激进:2.5(62.5%)
  • 保守:4.0(100%)

平均RT

  • 推荐:500ms
  • 激进:300ms
  • 保守:1000ms

并发线程数

  • 推荐:Tomcat最大线程数的70%
  • 示例:最大200线程,设置140

3. 监控告警

关键指标

# Prometheus告警规则
- alert: HighSystemLoad
  expr: system_load > 4.0
  for: 1m
  annotations:
    summary: "系统负载过高"

- alert: SystemProtectionTriggered
  expr: rate(sentinel_system_block_total[1m]) > 10
  for: 2m
  annotations:
    summary: "系统保护频繁触发"

4. 避免过度保护

问题:阈值设置过低,导致误判。

案例

  • 设置CPU阈值50%
  • 正常流量CPU就达到50%
  • 系统频繁触发保护
  • 大量正常请求被拒绝

解决

  • 通过压测确定合理阈值
  • 观察系统在正常和峰值流量下的指标
  • 阈值要留有余量(如峰值60%,设置80%)

5. 测试验证

@Test
public void testSystemProtection() {
    // 1. 配置系统保护规则
    SystemRule rule = new SystemRule();
    rule.setHighestCpuUsage(0.8);
    SystemRuleManager.loadRules(Collections.singletonList(rule));

    // 2. 模拟高负载
    // (需要真实的负载生成工具,如JMeter)

    // 3. 验证系统保护是否生效
    // 检查监控指标:CPU、限流次数等
}

注意事项

1. 系统保护是全局的

特点

  • 一旦触发,所有入口流量都会被限流
  • 不区分接口的重要性

影响

  • 可能导致核心接口也被限流
  • 需要配合接口级限流使用

2. Load指标的局限性

问题

  • 只支持Linux
  • Load受其他进程影响(如同机部署的其他服务)
  • Load的更新有延迟

建议

  • 配合CPU使用率使用
  • 或者只使用CPU使用率(跨平台)

3. BBR算法的冷启动

问题

  • 系统刚启动时,maxQps和minRt还没统计到
  • 可能导致误判

解决

  • 系统启动后先预热(发送少量请求)
  • 或者不使用Load保护,改用CPU/RT

4. 不要过度依赖系统保护

原则

系统保护是最后一道防线,不应该成为主要的保护手段。

正确的做法

  1. 首先做好接口级限流
  2. 其次做好依赖熔断
  3. 最后配置系统保护兜底

总结

本文我们学习了Sentinel的系统自适应保护机制:

  1. 五大保护指标:Load、CPU、RT、并发线程数、入口QPS
  2. BBR算法:根据系统容量动态限流,避免误判
  3. 配置方法:SystemRule配置,Dashboard可视化配置
  4. 实战案例:保护电商系统免于过载崩溃
  5. 三层防护:系统保护 + 接口限流 + 依赖熔断
  6. 最佳实践:选择指标、设置阈值、监控告警、避免过度保护

核心要点

系统保护是Sentinel的"核武器",它从整体角度保护系统不被压垮。但要谨慎使用,阈值设置要合理,避免误伤正常流量。

下一篇预告

我们将学习热点参数限流

  • 什么是热点数据?
  • 为什么普通限流无法保护热点?
  • 如何配置热点参数限流规则?
  • 秒杀商品、热门主播的保护方案

让我们一起探索更精细化的限流策略!