跳到主要内容

系统自适应保护

Sentinel 系统自适应保护从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

为什么需要系统自适应保护?

传统系统保护的问题

长期以来,系统保护的思路是根据硬指标(如系统 Load)来做系统过载保护。当系统 Load 高于某个阈值时,禁止或减少流量进入;当 Load 好转时,恢复流量进入。这种方式存在两个问题:

1. 延迟性

Load 是一个"结果"指标,根据 Load 调节流量始终有延迟。当前通过率的调整效果,至少要过 1 秒才能观测到。这导致曲线总是有抖动。

2. 恢复慢

当下游服务恢复后,应用 RT 减少,但由于 Load 仍然很高,通过率的恢复仍然缓慢,浪费了系统的处理能力。

Sentinel 的解决方案

Sentinel 借鉴了 TCP BBR(Bottleneck Bandwidth and RTT)算法的思想,根据系统能够处理的请求和允许进入的请求来做平衡,而不是根据间接指标(系统 Load)来做限流。

目标:在系统不被拖垮的情况下,提高系统的吞吐量。

工作原理

Sentinel 将系统处理请求的过程想象为一个水管:

请求进入 --> [水管] --> 请求处理完成
|
v
排队等待

当系统处理顺畅时,请求不需要排队,直接从水管中穿过,RT 最短。当请求堆积时,处理时间变为:排队时间 + 最短处理时间。

核心推论

推论一:如果能够保证水管里的水量能够让水顺畅流动,则不会增加排队的请求,系统负载不会进一步恶化。

设 T 为水管内部的水量,RT 为请求的处理时间,P 为进来的请求数,则一个请求从进入到处理完成,水管中存在 P * RT 个请求。当 T ≈ QPS * Avg(RT) 时,系统的处理能力和允许进入的请求个数达到平衡。

推论二:当保持入口流量是水管出来的流量的最大值时,可以最大利用水管的处理能力。

Sentinel 用 Load 作为启动自适应保护的因子,而允许通过的流量由处理请求的能力(请求的响应时间和当前系统正在处理的请求速率)来决定。

系统保护规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 Load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等维度监控应用指标。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求。

支持的保护模式

1. Load 自适应

系统的 Load1 作为启发指标,进行自适应系统保护。当系统 Load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时,才会触发系统保护。

系统容量由 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5

SystemRule rule = new SystemRule();
rule.setHighestSystemLoad(10.0); // Load1 阈值

SystemRuleManager.loadRules(Collections.singletonList(rule));

注意:Load 自适应仅对 Linux/Unix-like 机器生效。

2. CPU 使用率

当系统 CPU 使用率超过阈值时触发系统保护,取值范围 0.0-1.0。

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

SystemRuleManager.loadRules(Collections.singletonList(rule));

这种方式比较灵敏,适合对 CPU 敏感的应用。

3. 平均 RT

当单台机器上所有入口流量的平均 RT 达到阈值时触发系统保护。

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

SystemRuleManager.loadRules(Collections.singletonList(rule));

4. 并发线程数

当单台机器上所有入口流量的并发线程数达到阈值时触发系统保护。

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

SystemRuleManager.loadRules(Collections.singletonList(rule));

5. 入口 QPS

当单台机器上所有入口流量的 QPS 达到阈值时触发系统保护。

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

SystemRuleManager.loadRules(Collections.singletonList(rule));

系统规则属性

SystemRule 包含以下属性:

属性说明默认值
highestSystemLoadLoad1 阈值-
highestCpuUsageCPU 使用率阈值(0.0-1.0)-
avgRt所有入口流量的平均 RT 阈值-
maxThread入口流量的最大并发线程数-
qps所有入口流量的 QPS 阈值-

代码示例

完整的系统自适应保护示例

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.system.SystemRule;
import com.alibaba.csp.sentinel.slots.block.system.SystemRuleManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SystemProtectionDemo {

public static void main(String[] args) throws InterruptedException {
initSystemRules();

ExecutorService executor = Executors.newFixedThreadPool(50);

for (int i = 0; i < 200; i++) {
executor.submit(() -> {
Entry entry = null;
try {
// 入口流量,EntryType.IN
entry = SphU.entry("processRequest", EntryType.IN);

// 模拟业务处理
TimeUnit.MILLISECONDS.sleep(100);
System.out.println("请求处理成功");

} catch (BlockException e) {
System.out.println("系统保护触发: " + e.getClass().getSimpleName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (entry != null) {
entry.exit();
}
}
});

TimeUnit.MILLISECONDS.sleep(10);
}

executor.shutdown();
}

private static void initSystemRules() {
List<SystemRule> rules = new ArrayList<>();

// Load 自适应保护
SystemRule loadRule = new SystemRule();
loadRule.setHighestSystemLoad(10.0);

// CPU 使用率保护
SystemRule cpuRule = new SystemRule();
cpuRule.setHighestCpuUsage(0.8);

// 平均 RT 保护
SystemRule rtRule = new SystemRule();
rtRule.setAvgRt(500);

// 并发线程数保护
SystemRule threadRule = new SystemRule();
threadRule.setMaxThread(100);

// 入口 QPS 保护
SystemRule qpsRule = new SystemRule();
qpsRule.setQps(1000);

rules.add(loadRule);
rules.add(cpuRule);
rules.add(rtRule);
rules.add(threadRule);
rules.add(qpsRule);

SystemRuleManager.loadRules(rules);
}
}

Spring Boot 配置示例

spring:
cloud:
sentinel:
datasource:
system:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}-system-rules
groupId: DEFAULT_GROUP
rule-type: system

规则组合使用

系统保护规则可以组合使用,当任一规则触发时,请求都会被拒绝:

List<SystemRule> rules = new ArrayList<>();

// 组合多个规则
SystemRule rule1 = new SystemRule();
rule1.setHighestCpuUsage(0.8); // CPU 超过 80%

SystemRule rule2 = new SystemRule();
rule2.setAvgRt(1000); // 平均 RT 超过 1 秒

SystemRule rule3 = new SystemRule();
rule3.setMaxThread(200); // 并发线程超过 200

rules.add(rule1);
rules.add(rule2);
rules.add(rule3);

SystemRuleManager.loadRules(rules);

注意事项

1. 仅对入口流量生效

系统保护规则只对入口流量(EntryType.IN)生效。确保在定义资源时指定正确的 EntryType:

// 正确:入口流量
Entry entry = SphU.entry("resource", EntryType.IN);

// 错误:内部调用,不受系统保护规则影响
Entry entry = SphU.entry("resource", EntryType.OUT);

2. Load 自适应的局限性

Load 自适应算法对于非应用本身造成的 Load 高的情况(如其他进程导致的不稳定),效果不明显。

3. CPU 使用率的计算

CPU 使用率的统计有一定开销,在高性能场景下需要权衡。

4. 与其他规则的关系

系统保护规则是兜底规则,建议与流量控制规则、熔断降级规则配合使用。

最佳实践

1. 合理设置 Load 阈值

  • 参考值:CPU 核心数 * 2.5
  • 根据实际业务特点调整
  • 观察系统在正常负载下的 Load 水平

2. 组合使用多种规则

// 推荐组合
- CPU 使用率保护(灵敏度高)
- 平均 RT 保护(响应时间保障)
- 并发线程数保护(防止线程池耗尽)

3. 监控和调优

  • 定期查看监控数据,了解系统负载情况
  • 根据实际情况调整阈值
  • 设置告警,及时发现异常

4. 压测验证

在生产环境使用前,通过压测验证规则配置是否合理:

  • 确定系统的最大处理能力
  • 验证规则是否能有效保护系统
  • 观察系统在极限情况下的表现

与其他保护机制的关系

保护机制维度作用
流量控制资源维度控制单个资源的访问量
熔断降级资源维度处理不稳定依赖
系统自适应保护系统维度保护整个应用不被拖垮

系统自适应保护是最后一道防线,当其他保护机制失效时,它能够保护系统不被拖垮。

下一步