跳转至

LiteLLM Cooldown / 冷却期 全链路文档

本系列文档完整描述 LiteLLM Router 的 cooldown(冷却期)机制:什么时候触发、写到哪里、路由层怎么读、多久恢复、跟 num_retries / fallback / health check 怎么互动。

适用场景: - 排查"上游某个 deployment 挂了,proxy 怎么自动避让" - 设计多区域 / 多 key 负载均衡时理解失败逻辑 - 调整 cooldown_time / allowed_fails 等参数前理解默认行为 - 排查"为什么我设了 max_budget,超了还能调"——cooldown 不是这个语义(详见 docs/rate-limiting/

⚠️ 常见误解:cooldown ≠ health check ≠ rate limiting。 - Cooldown:单个 deployment 短期失败 → 临时跳过这个 deployment(路由层) - Health check:定期主动 ping → 写 /health 端点 + DB 表(不影响路由) - Rate limiting:用户额度超了 → 拒绝请求(auth 层)

三者互不耦合。详见 04-troubleshooting.md §跟其它机制的关系.


文档目录

文件 内容
01-mechanism.md 触发条件第一关 / 第二关(V2 默认 + V1 allowed_fails)、写入 CooldownCache、路由层读取过滤、Redis key 格式、TTL 自然恢复、Prometheus 通知
02-config-reference.md YAML router_settings / litellm_settings / model_info 单 deployment / 环境变量 / cooldown_time 优先级 4 层
03-best-practices.md 默认 5 秒太短的问题 / 推荐配置 / 跟 num_retries/fallbacks / latency-based-routing 的叠加 / 常见误区
04-troubleshooting.md 6 类常见现象速查 / single deployment 特殊行为 / wildcard 永不 cooldown / allowed_fails 是 InMemoryCache 陷阱 / 跟其它机制对比 / 状态可观测性
05-git-history.md Diff in Cooldown — 变更时间线 / 关键里程碑 / 文件级变更详情 / 最近半年变更趋势 / 核心贡献者

整体架构一览

flowchart TB
    subgraph "失败方:业务请求"
        Req["业务请求<br/>(用户 / 内部调用)"]
        LLM["上游 LLM 返回失败<br/>5xx/429/401/408/404"]
    end

    subgraph "Router 失败回调<br/>litellm/router.py"
        FailureCB["deployment_callback_on_failure<br/>:5740-5798"]
        PreCheck["async_pre_call_check<br/>RateLimitError 分支<br/>:6040-6089"]
    end

    subgraph "Cooldown 决策<br/>cooldown_handlers.py"
        Gate1{"_is_cooldown_required<br/>状态码白名单<br/>:40-95"}
        Gate2["_should_cooldown_deployment<br/>:166-257"]
        Mode1{"V2 默认模式<br/>4 条触发路径"}
        Mode2{"V1 allowed_fails 模式<br/>累计计数"}
        Skip[直接放行]
    end

    subgraph "存储层"
        CC["CooldownCache (DualCache)<br/>cooldown_cache.py"]
        Redis["Redis<br/>key: deployment:&lt;id&gt;:cooldown<br/>TTL=cooldown_time"]
        Mem["InMemoryCache<br/>(60s 本地缓存)"]
    end

    subgraph "Router 选 deployment<br/>litellm/router.py"
        ChooseDep["async_get_healthy_deployments<br/>:8523-8531"]
        Filter["_filter_cooldown_deployments<br/>:9202-9223"]
        AllStrategies["所有 routing_strategy 共用<br/>(latency/lowest_tpm/simple_shuffle/...)"]
    end

    subgraph "通知"
        Callback["asyncio.create_task<br/>router_cooldown_event_callback"]
        Prom["Prometheus:<br/>litellm_deployment_cooled_down"]
    end

    Req --> LLM
    LLM --> FailureCB
    LLM -.RateLimit 提前.-> PreCheck

    FailureCB --> Gate1
    PreCheck --> Gate1
    Gate1 -- "白名单内" --> Gate2
    Gate1 -- "其他错误" --> Skip
    Gate2 --> Mode1
    Gate2 --> Mode2
    Mode1 -- "命中任一路径" --> CC
    Mode2 -- "fails > 阈值" --> CC
    Mode1 -- "都不命中" --> Skip
    Mode2 -- "未达阈值,只 +1" --> Skip
    CC --> Redis
    CC --> Mem
    CC --> Callback
    Callback --> Prom

    Req2["下一次业务请求"] --> ChooseDep
    ChooseDep --> Filter
    Redis -. async_batch_get_cache .-> Filter
    Filter --> AllStrategies
    AllStrategies --> Skip2[选可用 deployment 发请求]

    style Gate1 fill:#fdd,stroke:#c33,stroke-width:2px
    style Gate2 fill:#fdd,stroke:#c33,stroke-width:2px
    style Filter fill:#dfd,stroke:#3a3,stroke-width:2px
    style CC fill:#ffd,stroke:#aa3

几点关键事实

  1. 触发只发生在 2 个位置:业务请求 failure callback(router.py:5782)+ pre-call rate-limit check(router.py:6066)。心跳失败不触发 cooldown
  2. 过滤只发生在 1 个位置router.py:9202-9223 _filter_cooldown_deployments。所有 routing strategy(latency-based / lowest-tpm / simple-shuffle / cost-based 等)共用这个过滤器。
  3. 没有"主动恢复"逻辑:Redis key TTL 自然过期 → 该 deployment 重新进备选池。下一次业务请求过来如果撞上它失败,再次 cooldown。
  4. 存储用 DualCache:内存层 60s 缓存 + Redis 层。多 Pod 共享 Redis,所以一个 Pod cooldown 一个 deployment,其他 Pod 也跳过。
  5. allowed_fails 计数器不是 DualCache 而是 InMemoryCache(router.py:487-489)—— 多 Pod 间不共享!见 04 §4.

速查:默认行为

维度 默认值 说明
DEFAULT_COOLDOWN_TIME_SECONDS 5 TTL 短得激进,强调"快速试错快速恢复"
触发模式 V2 默认(基于失败率) YAML 没设 allowed_fails
触发状态码 401/404/408/429/5xx 4xx 其它(400/403/422 等)触发
disable_cooldowns False 全局可关
数据存储 DualCache(内存 60s + Redis 5s) 多 Pod 共享 Redis 列表
Prometheus 指标 litellm_deployment_cooled_down 累计计数器

触发条件速查(V2 默认模式)

flowchart LR
    Fail[失败请求<br/>状态码白名单已通过]
    Path1{"429 + 多 deployment ?"}
    Path2{"100% 失败<br/>且当分钟流量 ≥1000 ?"}
    Path3{"失败率 >50%<br/>且当分钟流量 ≥5<br/>且多 deployment ?"}
    Path4{"litellm._should_retry<br/>== False<br/>(永久错误如 401)?"}
    Skip[不 cooldown]
    Cool[★ Cooldown 该 deployment<br/>5 秒]

    Fail --> Path1
    Path1 -- 是 --> Cool
    Path1 -- 否 --> Path2
    Path2 -- 是 --> Cool
    Path2 -- 否 --> Path3
    Path3 -- 是 --> Cool
    Path3 -- 否 --> Path4
    Path4 -- 是 --> Cool
    Path4 -- 否 --> Skip

    style Cool fill:#fdd
    style Skip fill:#dfd

4 条触发路径任一命中即 cooldown。代码见 cooldown_handlers.py:166-257 _should_cooldown_deployment

详细每条路径的实际触发场景见 01-mechanism.md §3.


决策树:你应该看哪一篇?

flowchart TD
    Q{你的问题是?}

    Q -->|"想理解整体怎么跑"| A1[01-mechanism]
    Q -->|"配置在哪里写、有哪些字段"| A2[02-config-reference]
    Q -->|"想调优(默认 5s 太短/想加 allowed_fails)"| A3[03-best-practices]
    Q -->|"线上 deployment 没自动避让/避让过度"| A4[04-troubleshooting]
    Q -->|"想知道跟 health check 啥区别"| A4

    style Q fill:#cef,stroke:#369

一分钟自检

如果你怀疑当前 proxy 的 cooldown 行为不符预期:

1. 你 router 走的是哪个模式?

grep -E "cooldown_time|allowed_fails|allowed_fails_policy|disable_cooldowns" \
  proxy_server_config.yaml
  • 啥都没设 → V2 默认模式,5 秒 cooldown,按失败率触发
  • 设了 allowed_failsallowed_fails_policyV1 模式,按累计失败次数触发

2. cooldown 在不在工作?

业务请求触发一次失败(比如改个错误 api_key 强制 401)后立刻看 Redis:

.venv312/bin/python -c "
import os, redis
r = redis.Redis(host=os.environ['REDIS_HOST'], port=int(os.environ['REDIS_PORT']),
                password=os.environ.get('REDIS_PASSWORD'))
print('cooldown keys:', r.keys('deployment:*:cooldown'))
"
  • 看到 deployment:<uuid>:cooldown key → cooldown 工作正常
  • 没看到 → 看 04 §1 排查

3. cooldown 持续多久?

.venv312/bin/python -c "
import os, redis, time
r = redis.Redis(host=os.environ['REDIS_HOST'], port=int(os.environ['REDIS_PORT']),
                password=os.environ.get('REDIS_PASSWORD'))
keys = r.keys('deployment:*:cooldown')
for k in keys:
    print(f'{k.decode()}  TTL={r.ttl(k)}s')
"

TTL 应该 ≤ 你配的 cooldown_time(默认 5)。


跟 Health Check 的对比(再次明确)

维度 Cooldown Health Check
触发 业务请求失败 proxy 主动 ping
影响 路由决策(跳过该 deployment) /health 端点 + DB 表
默认时长 5 秒 30s(tick)or 5min(真实 ping)
多 Pod 协调 Redis 共享 Redis 共享(详见心跳文档 §3
恢复 TTL 自然过期 下一次心跳
真正防护业务

真正在 prod 防止用户撞墙的是 cooldown,不是心跳


三个最常用配置组合

A. 默认(你们 prod 当前)

# 啥都没设,走 V2 默认逻辑
# cooldown_time = 5s
# 触发: 失败率 >50% 且当分钟 ≥5 个请求

代价:单次抽风可能让该 deployment 进 cooldown(路径 1.4 永久错误立即冷)。但 5 秒就恢复,影响很小。

B. 推荐(小流量服务)

router_settings:
  cooldown_time: 30      # 5s → 30s,失败 deployment 30 秒内不再被选

代价:上游真挂时,"无效失败请求"频率从每 5s → 每 30s。但小流量服务 prod 上其它 deployment 充裕,不影响业务。

C. 大流量 / 多 deployment 部署

litellm_settings:
  allowed_fails_policy:                # 启用 V1 累计计数模式
    AuthenticationErrorAllowedFails: 3
    TimeoutErrorAllowedFails: 50
    RateLimitErrorAllowedFails: 100
    InternalServerErrorAllowedFails: 20

router_settings:
  cooldown_time: 60
  num_retries: 2                       # 业务请求自动 retry,失败再算 cooldown

代价:偶发抽风不会立即 cooldown,"累计失败 N 次"才冷。需要监控失败率别忘了。

详细推荐组合见 03-best-practices.md §推荐配置.


关于本系列文档

  • 行号引用基于 master 分支同步点(2026-05),上游可能漂移,遇到 grep 不到时按文件名 + 函数名定位
  • 实战经验来自本项目 prod / dev 调研,不是搬运上游 docs
  • 上游 cooldown 文档:https://docs.litellm.ai/docs/routing (有提及但不深入)