Priority、routing key 与 waiting queue 重排#
这章解决什么问题#
前面的调度与内存章节已经解释了 batch 怎么形成、内存不够时怎样 retract、allocator 和 cache 怎样共同工作,但还缺一层更贴近“请求在真正进入 batch 之前怎样被排队重排”的逻辑:priority scheduling、routing key 亲和、FCFS、最长输出优先、随机化,以及 cache-aware policy 之间到底怎样组合。
如果不把这层讲出来,读者会知道 scheduler 最终拿到了一个 batch,却说不清 waiting queue 在这之前经历了哪些策略性重排。
为什么 waiting queue 排序值得单独成章#
schedule_policy.py 并不是一个边缘优化文件。它直接决定:
- waiting queue 先按什么标准排序
- priority 是否参与排序
- routing key 是否尝试与 running batch 保持亲和
- cache-aware policy 在队列很大时是否切换策略
也就是说,这层逻辑虽然没有直接 forward 模型,但它显著改变了后续 batch 的形状与资源行为。
一张图:请求从 waiting queue 到 batch 之前,会先被哪几层策略加工#
这张图解决的理解障碍是:很多读者会把 waiting queue 想成普通 FIFO 队列,但源码实际是“队列 + 多层重排策略”。
flowchart LR
WQ["waiting queue"] --> Pri["priority / FCFS"]
Pri --> Cache["cache-aware policy"]
Cache --> Route["routing-key affinity"]
Route --> Batch["selected batch"]这张图比纯文字多解释的一点是:batch 的形状不是只由请求到达顺序决定,而是由多层排序策略共同塑形。
SchedulePolicy.calc_priority(...) 在做什么#
这是阅读这层逻辑最好的入口。它会先看:
- 是否启用 priority scheduling
- 当前 active policy 是什么
- 队列规模是否触发 policy 切换
- 是否需要按 routing key 对 waiting queue 做二次重排
也就是说,calc_priority(...) 不是“算一个 priority 数值”,而更像“对 waiting queue 做一次整体重整”。
priority scheduling 的语义并不止一个方向#
schedule_policy.py 里有:
enable_priority_schedulingschedule_low_priority_values_firstpriority_sign
这说明 priority 本身不是固定语义。系统允许:
- 数值越小优先级越高
- 或数值越大优先级越高
这点非常值得技术书写明,因为它直接影响你对配置和日志的解释。如果忽略这层,维护者很容易把“高优先级被饿死”误判成 bug,而实际只是优先级方向理解反了。
_sort_by_priority_and_fcfs(...) 说明了什么#
这段逻辑很直接:当 priority scheduling 开启时,waiting queue 会先按 priority 排,再按 received timestamp 排。也就是说,FCFS 不是消失了,而是退到了同优先级内的次级规则。
这是典型的好系统设计:
- priority 决定大方向
- FCFS 决定同档次内部的稳定顺序
_sort_by_routing_key(...) 为什么特别像“队列亲和策略”#
这段逻辑会统计当前 running batch 里的 routing_key 分布,再对 waiting queue 做偏好性排序,把与当前 batch 更匹配的 request 提前。换句话说,这不是单纯的业务标签统计,而是在尝试让同一 routing key 的请求更有机会被聚到相邻执行阶段里。
这类设计很值得成书,因为它体现的是“调度策略不只看单个请求,还看它与当前运行集合的关系”。
cache-aware policy 为什么会和 waiting queue 大小绑定#
_determine_active_policy(...) 里会根据 waiting queue 大小调整实际启用的 cache-aware policy。这说明策略本身也承认:
- 小队列时,某些更复杂的排序不一定值得
- 大队列时,不做 cache-aware 或 routing-aware 重排可能浪费很多机会
这是一种典型的工程性折中:不是永远用“最聪明”的策略,而是按问题规模决定策略强度。
priority preemption 又和 waiting queue 有什么关系#
scheduler.py 里除了 waiting queue 重排,还会在某些条件下考虑“更高优先级请求是否值得抢占已有等待项或运行项”。这说明 priority 不只影响排序,也可能影响是否直接挤掉较不重要的请求。
因此,priority 在 SGLang 里不是轻量标签,而是一整套涉及:
- 排队顺序
- 抢占阈值
- abort / preempt 行为
的控制面语义。
routing key 与 placement signal 怎样对上#
前面扩展与调试章节已经写过 /v1/loads 和 routing key metrics;这里则是它在调度层的落点。两者对照起来读,会非常清楚:
- 一边是 scheduler 用 routing key 做排序/亲和
- 一边是 observability 用 routing key 看分布是否符合预期
这是一种非常像好技术书的前后回扣:调度层解释“为什么这样排”,维护层解释“排完以后看到了什么”。
这一层最容易出现的误判#
1. 把 waiting queue 当成简单 FIFO#
源码里它显然会被多层策略重排。
2. 以为 priority 只是一个附加字段#
实际上它会影响排序方向、FCFS、甚至抢占。
3. 以为 routing key 只服务观测层#
调度策略本身就会消耗它。
如果你怀疑问题出在等待队列排序,先怎么查#
建议按这个顺序:
- 看当前是否启用了 priority scheduling,以及优先级方向是哪一边。
- 看 active cache-aware policy 是什么。
- 看 running batch 当前持有什么 routing key。
- 看 waiting queue 在排序前后 key 分布有没有明显变化。
- 再决定问题是配置、策略本身,还是 placement signal 的来源出了偏差。
小结#
这一章真正想补齐的,是调度主线里经常被忽略的“队列塑形层”:
- waiting queue 不是普通 FIFO。
- priority、FCFS、cache-aware policy 和 routing key 亲和一起决定了 batch 的前置形状。
- 只有看清这层,才能真正理解 scheduler 为什么会挑中“这些请求”,而不是另外一些。
叶王 © 2013-2026 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。