AI 网关请求排队公平调度与租户防饿死落地

日期:2026-07-30

一、项目背景

这个项目的客户是一家集团型企业,十几个业务部门通过统一的 AI 网关调用后端的自托管大模型集群。矛盾在一次月度复盘会上爆发:营销部门抱怨他们的实时文案生成经常超时,而数据部门的批量打标任务却跑得欢——排查后发现,数据部门的作业脚本每次并发提交上千个请求,把网关队列灌满,营销部门的零星请求排在几百号开外,等到轮上早就过了业务侧的超时线。运维当时的手段只有全局限流和按租户配额两板斧:全局限流一刀切,高峰期大家一起卡;租户配额治标不治本,配额内的瞬时突发照样能把队列灌满。更尴尬的是,"公平"没有量化定义,每次扯皮都变成部门间的声量比拼。

我们采用的 AI 网关具备接入层与路由层的基础能力,这个项目是在其上实现一套加权公平排队与防饿死机制,项目周期约两个月。

二、落地场景

改造后的网关排队模型是这样的:每个租户(部门/应用)有独立的逻辑子队列,调度器按加权公平算法从各子队列取请求下发给后端模型实例——权重由租户等级决定,但任何租户都无法通过灌入海量请求挤占其他租户的出队机会,因为出队按权重轮转而不是按到达顺序。突发大批量请求只会加长自己子队列的深度,不影响别人。

在此之上叠加了等待预算机制:每个请求入队时携带业务侧的超时容忍度(实时类默认 5 秒、交互类 15 秒、批量类 300 秒),调度器持续预估每个请求的预计出队时间,预估超过等待预算的请求会被提前处置——要么触发防饿死提升(临时提高其出队优先级),要么快速失败返回明确的"系统繁忙请稍后"(好过让调用方傻等到超时)。防饿死提升有全局预算上限,防止提升机制本身被滥用。

高峰降级预案也做进了排队体系:当整体队列水位超过红线,网关按预先声明的降级顺序动作——先暂停批量类租户的出队、再对交互类请求启用截断上下文的轻量模式、最后才对实时类限流。每一步都有明确的水位触发线和自动恢复线,不再依赖运维手工拉闸。

三、关键技术挑战与解决思路

第一个挑战是加权公平算法在大模型场景的适配。经典的加权公平队列按请求个数轮转,但大模型请求的资源消耗差异极大——一个 200 token 的分类请求和一个 8000 token 的长文生成,对后端的占用完全不同。按个数公平实际上是不公平。我们把公平的计量单位从"请求数"改成"预估 token 成本":入队时按输入长度加输出上限预估成本,出队轮转按各租户已消耗的成本额度计算虚拟时间,长请求多的租户自然轮转得慢。预估和实际的偏差在请求完成后回填修正,保证长期公平收敛。

第二个挑战是等待预算的预估精度。预计出队时间取决于前面有多少请求、后端吞吐多少,两者都在波动。初版用简单的队列深度除以平均吞吐,高峰期误差大到没法用。改进版按租户子队列分别建模,吞吐用滑动窗口的分位数而不是均值(大模型吞吐受长请求影响呈长尾分布),并且把后端实例的健康状态纳入折算——有实例被摘除时预估立即变悲观。精度上来之后,快速失败的决策才有底气:告诉调用方"现在不行",前提是这个判断基本靠谱。

第三个挑战是防饿死与整体吞吐的权衡。防饿死提升本质上是插队,插队多了吞吐必然受损。我们给提升设了全局预算:任意时刻被提升的请求不超过在途总量的 10%,且同一租户的提升次数按小时限额。压测时故意构造极端场景——一个大租户持续灌入长请求、三个小租户零星提交实时请求——验证小租户请求成功率能稳在目标线上,同时整体吞吐损失控制在 5% 以内。这组压测数据后来成了和各部门对齐"公平定义"的依据:公平不是绝对平均,是每个租户都有可预期的服务下限。

案例片段(已脱敏):加权公平排队的核心配置——yaml fair_queue:  cost_unit: estimated_tokens        # 按 token 成本公平,非请求数  tenants:    - { name: marketing,  weight: 30, class: realtime,    wait_budget_s: 5 }    - { name: cs_bot,     weight: 30, class: interactive, wait_budget_s: 15 }    - { name: data_batch, weight: 15, class: batch,       wait_budget_s: 300 }  anti_starvation:    boost_share_max: 0.10            # 提升请求占在途上限    per_tenant_boost_hourly: 200  degrade:    - { level: L1, trigger: queue_water > 0.7, action: pause_batch }    - { level: L2, trigger: queue_water > 0.85, action: light_mode_interactive }    - { level: L3, trigger: queue_water > 0.95, action: throttle_realtime }一次大促压测的记录:数据部门灌入 5000 个批量请求后,营销租户的 P95 等待从改造前的 47 秒降到 1.9 秒,饿死(等待超预算被放弃)请求数从 312 个降到 0,整体吞吐损失约 4%。

四、效果数据

上线三个月后的数据(脱敏示意值):小租户(实时与交互类)请求成功率从高峰期的约 82% 提升到 99% 以上;队列等待 P95 从高峰 30 秒以上降到 3 秒以内;饿死请求数从月均数千个降到基本归零;快速失败机制上线后,业务侧无效等待时间大幅减少,调用方超时重试引发的放大流量下降约 70%;三次真实高峰触发了 L1 降级(暂停批量出队),全部自动恢复,运维零人工介入;整体集群吞吐相比改造前基本持平(公平机制的开销被重试风暴的消失抵消)。部门间关于"谁挤占谁"的扯皮,在有了每租户的等待与成功率报表之后基本消失。

五、可复用经验总结

第一,大模型网关的公平计量单位必须是 token 成本而不是请求数,否则长请求租户会隐性挤占所有人。第二,等待预算加快速失败优于无限排队,明确拒绝比模糊等待对调用方更友好,但前提是出队时间预估要靠谱。第三,防饿死要设全局预算,插队机制本身必须防滥用。第四,降级预案要声明式地做进排队体系,水位触发、自动恢复,把运维从拉闸决策里解放出来。第五,公平要有报表——每租户的等待分布和成功率摆在桌面上,组织内耗自然下降,这是技术机制之外最大的收益。