大模型推理延迟敏感调度与关键请求优先落地

日期:2026-08-27

一、项目背景

我们的推理集群早期对所有请求一视同仁,公平排队。问题出在业务混合上:实时对话和批量摘要抢同一批显卡,批处理那种一长串的摘要任务,把对话的首字延迟拖到好几秒。结果就是客服的实时应答被闲时的批量流量误伤,用户体验直接崩。更要命的是大促期间,营销刷屏的批量生成把算力打满,真正付费用得急的对话反而排不上,客服主管那段时间天天在群里@我们。

我们当时盯监控,发现一个诡异现象:显卡利用率并不低,可对话的首字就是上不去。后来才看清,是几个长摘要任务占着卡做连续批处理,把短平快的对话请求全堵在队列后面。这件事给我们的教训是,公平不等于合理,不同业务的延迟敏感度天差地别,用同一把尺子量,吃亏的永远是那个最怕慢的。

二、落地场景

我们给请求打了延迟敏感度标签,实时对话、客服应答这类归为高敏感,走优先级队列;批量摘要、离线生成归为低敏感,平时跑、忙时退避。关键业务还能抢占,高敏感请求进来,正在跑的低敏感批处理任务会被礼貌地挂起,让出算力。低敏感任务有保底带宽,不会被彻底饿死。延迟看板实时展示各队列的 P99 首字,运维一眼能看出哪路堵了,再不用翻一堆容器日志去猜。

为了不把抢占搞成灾难,我们把保底带宽做成可调参数,夜里闲时给批处理多放,白天高峰收紧。业务方也能在管控台看到自己请求的敏感度归属,有异议走申请流程,而不是偷偷改标签。

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

分级本身不难,难在抢占的度。我们一开始把高敏感设得太狠,只要有对话进来就抢,结果批处理任务大面积饿死,长任务永远跑不完,资源利用率反而掉了。后来给低敏感加了保底带宽,保证它每周期至少能推进一点,才两全。另一个坑是敏感度打标,业务方都想把自己的请求标成高敏感,标错了等于没分级。我们改成由网关侧按接口画像自动打,业务方只能申请调整,不能自己乱贴。

还有尾延迟,批量任务被抢占后重算的代价要算进去,不能为了首字好看把尾延迟搞炸。我们加了一个尾延迟守卫,被抢占任务重算次数过多时自动降级处理,避免单个长任务反复被打断。这套机制上线前我们内部吵了很久该不该抢占,最后用数据说话,保底带宽一加上,反对意见自己就消了。

案例片段(已脱敏): 延迟敏感分级与抢占调度的核心参数如下:yaml scheduler:  classes: [realtime, batch]  realtime:    priority: 10    preempt: true  batch:    priority: 1    guaranteed_bandwidth: "30%"   # 低优保底,防止饿死  tail_guard: true                # 抢占后重算代价受控调优后,关键请求 P99 首字从 4.2s 降到 0.8s,批处理任务完成率保持在 97% 以上,尾延迟无异常抬升。

我们后来把延迟看板和自动扩缩容联动起来,P99 一越线就触发底座加实例,而不是等人工看。这个联动一开始不敢全开,怕误判导致频繁扩缩,后来加了滑动判断,连续三次越线才动作,抖动被滤掉。实测下来,高峰时自动扩容比人工快了四五分钟,那几分钟刚好卡在用户体验的临界点上,值。

四、效果数据

关键请求的 P99 首字从约 4.2 秒降到 0.8 秒,客服实时应答的体验彻底翻身,那段时间群里不再有人@我们。低敏感任务的等待时长虽然上升,但完成率保持在九成七以上,没有任务被彻底饿死。抢占次数在高峰时段每分钟约两百次,属于可控范围。整体吞吐相比纯公平排队反而提升了约一成,因为高敏感的小请求不再被长任务堵在后面。运维第一次能按业务而非按机器看延迟,排障方向清楚多了,平均定位时间从半小时缩到几分钟。

五、可复用经验总结

推理调度不能一刀切排队,按业务敏感度分队列是基本功,但抢占一定要留保底,否则你优化了一个指标,搞崩了另一个。敏感度打标权要收口到平台侧,业务方天然会把自己的请求往高里标,放开就废了,这点我们是用一次混乱换来的教训。尾延迟和首字延迟要一起看,只优化首字、不顾被抢占任务重算的代价,是典型拆东墙补西墙。保底带宽做成可调,跟着业务节奏走,没有一劳永逸的值。说实话,调度这东西没有完美参数,只有和业务匹配的参数,把决策权交还给数据,团队反而少吵架。

业务方的反馈也变了。以前他们只关心能不能用,现在会拿着看板问为什么我这批请求被升级了,开始理解调度在替所有人平衡。这种认知的转变,比指标本身更让我们踏实,因为意味着抢占不再被当成黑盒,而是可解释的资源分配。

结语

后来我们把保底带宽做成了按天时段自动调的策略,夜里闲时给批处理多放点,白天高峰收紧,资源曲线平滑了不少。调参这件事,永远是跟着业务节奏走,系统能做的,是把"该调"这件事变成一键而不是凭经验拍板。