什么是自适应限流?

#中间件#面试八股

什么是自适应限流?

结论:自适应限流(Adaptive Rate Limiting)不预设固定 QPS 阈值,而是根据系统实时指标(CPU、load、RT、并发线程数、排队长度)动态计算当前能承受的速率。它解决的是「固定阈值在容量变化时要么过松要么过严」的问题,代价是算法复杂、容易抖动。

一、固定阈值为什么不够

场景固定阈值的表现
下游变慢(DB RT 从 5 ms 涨到 50 ms)阈值不变,线程池被打满,雪崩
扩容一半机器总阈值需人工改配置,改慢了就过载
大促冷启动按全量阈值放行,直接压垮刚启动的实例
慢 SQL 偶发指标早已恶化,固定阈值毫无感知

二、反馈环

      ┌───────────────────────────────────────────┐
      │                                           │
      ▼                                           │
 ┌─────────┐   放行速率   ┌──────────┐   观测指标   │
 │ 限流器   │ ──────────► │   系统    │ ───────────┤
 │(动态阈值)│             │ CPU / RT │             │
 └─────────┘ ◄────────────│ 并发/队列 │             │
                调整阈值    └──────────┘             │
                                                   │
   阈值 = f(CPU, load1, RT_p99, inflight, queue) ───┘

两条主流思路:

  1. 基于负载:CPU 超过 80% 或 load 超过核数就线性降额(Sentinel 系统规则)。
  2. 基于时延:用 Little's Law(并发 = QPS × RT)估算容量,RT 上升就压低 QPS(BBR 类算法)。

三、Sentinel 系统自适应

system:
  - highestSystemLoad: 4.0        # load1 超过 4 开始降
    avgRt: 200                    # 平均 RT 超过 200ms 开始降
    maxThread: 500                # 并发线程数上限
    qps: 3000                     # 入口总 QPS 上限
    checkIntervalMs: 1000

Sentinel 会取这些指标中最严格的一个,动态压低准入阈值。它的取向是「宁可少放一点,也不让系统进入不可恢复的过载」。

四、基于时延的自适应实现

public class AdaptiveLimiter {
    private final AtomicLong inflight = new AtomicLong();
    private volatile double limit = 1000;          // 初始容量估计
    private volatile long minRt = Long.MAX_VALUE;
    private volatile long maxRt = 0;
    private final AtomicLong sample = new AtomicLong();

    public boolean tryAcquire() {
        return inflight.get() < limit;
    }

    public void onEnter() {
        inflight.incrementAndGet();
    }

    public void onExit(long rtMs) {
        inflight.decrementAndGet();
        minRt = Math.min(minRt, rtMs);
        maxRt = Math.max(maxRt, rtMs);
        if (sample.incrementAndGet() % 100 == 0) {  // 每 100 次采样调整一次
            // RT 翻倍说明排队严重,容量砍半;否则小幅上调
            double adjust = maxRt > minRt * 2 ? 0.5 : 1.05;
            limit = Math.max(50, Math.min(10000, limit * adjust));
            log.info("自适应阈值调整 limit={} minRt={} maxRt={}", limit, minRt, maxRt);
        }
    }
}

生产实现还要加平滑(滑动平均)、滞回(上调慢、下调快)与最小样本数限制。

五、常见追问

Q1:自适应限流会不会抖动? 会。指标本身有噪声,按瞬时值直接调整会导致阈值震荡。工程上用滑动平均、限制调整步长、上调慢下调快来抑制。

Q2:和自动扩缩容什么关系? 自适应限流保护现有容量,扩缩容增加容量。限流先扛住,扩容跟上后再放开阈值,二者联动,否则扩容期间会被限流误伤。

Q3:怎么验证自适应有效? 压测时人为注入慢查询或降低下游能力,观察系统是否在 RT 上升后自动降低吞吐而不崩溃,同时看限流触发次数与业务成功率的变化。

Q4:Little's Law 怎么用? 并发数 = QPS × RT。已知线程池上限是 500、RT 是 100 ms,则理论 QPS 上限为 5000;RT 涨到 200 ms,上限自动降到 2500。

小结

  • 自适应限流把固定阈值换成按实时指标计算的阈值,用来应对容量漂移。
  • 两条主线:基于负载(CPU、load)与基于时延(Little's Law、BBR)。
  • 必须抑制抖动:平滑、限制步长、上调慢下调快。
笔记加载中…