系统自适应保护
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 包含以下属性:
| 属性 | 说明 | 默认值 |
|---|---|---|
| highestSystemLoad | Load1 阈值 | - |
| highestCpuUsage | CPU 使用率阈值(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. 压测验证
在生产环境使用前,通过压测验证规则配置是否合理:
- 确定系统的最大处理能力
- 验证规则是否能有效保护系统
- 观察系统在极限情况下的表现
与其他保护机制的关系
| 保护机制 | 维度 | 作用 |
|---|---|---|
| 流量控制 | 资源维度 | 控制单个资源的访问量 |
| 熔断降级 | 资源维度 | 处理不稳定依赖 |
| 系统自适应保护 | 系统维度 | 保护整个应用不被拖垮 |
系统自适应保护是最后一道防线,当其他保护机制失效时,它能够保护系统不被拖垮。
下一步
- 控制台 - 学习 Sentinel 控制台的部署和使用
- Spring Cloud 整合 - 与 Spring Cloud Alibaba 整合