/v1/loads、routing key 与 placement signal#

这章解决什么问题#

扩展与调试部分已经讲了 metrics、watchdog、request stage、request-level 证据链,但还缺一类对平台工程特别关键的信号:如何从 /v1/loads、routing key 统计、routed_dp_rank 和 worker load metrics 判断系统当前“把请求放到了哪里、负载是不是在往你预期的方向流”。

这一章就是把这些 placement signal 收成一条维护者可用的负载证据链。

为什么这层和普通 metrics 不一样#

普通 metrics 更偏“系统现在忙不忙、快不快”;而 /v1/loads 和 routing signal 更偏“系统把请求分配到了哪里、某类请求是否被聚到一起、当前 worker 之间是否失衡”。这是一层更接近 placement / routing 的观测面。

对多 DP、外部 router、租户隔离或 routing key 场景来说,这层比单纯 TTFT/QPS 更有诊断价值。

一张图:placement signal 来自哪几层#

这张图解决的理解障碍是:很多人会把 /v1/loads 看成单一接口,但它其实综合了 controller、scheduler 和请求对象上的 routing 信息。

flowchart LR
    Req["request with routing_key / routed_dp_rank"] --> DPC["DataParallelController"]
    DPC --> SCH["scheduler stats / load metrics"]
    SCH --> Loads["GetLoadReq / GetLoadsReq -> /v1/loads"]
    Req --> Obs["routing key metrics / active gauges"]

图比纯文字多解释的一点是:placement signal 不是某个单点 API 生成的,而是请求字段、调度层统计和控制面共同形成的。

/v1/loads 真正提供了什么#

entrypoints/v1_loads.pyscheduler_metrics_mixin.py 说明,/v1/loads 不是“再包装一次 Prometheus 指标”,而是提供更接近 scheduler 内部状态的聚合视图。它可以返回:

  • core metrics
  • memory
  • spec
  • lora
  • disagg
  • queues

这使它特别适合作为运行时排障时的“状态快照 API”。

GetLoadsReqInput / GetLoadsReqOutput 为什么值得记#

io_struct.py 里,这两个对象明确对应 /v1/loads 这条控制面路径。这说明 load inspection 不是外围工具附加能力,而是 manager 之间正式传递的一种请求/响应对象。

也正因为这样,load 视图可以跨进程、跨 rank 收集,而不是只能在本地线程里临时读变量。

routing key 统计在解释什么#

metrics_collector.py 里会维护:

  • num_unique_running_routing_keys
  • routing_key_running_req_count
  • routing_key_all_req_count

scheduler_metrics_mixin.py 里又会从 running/waiting 请求里提取 routing_key 去计算这些统计。也就是说,系统并不把 routing key 当作纯请求标签,而是把它当成“当前调度分布是否符合预期”的正式观测量。

这对平台工程很重要,因为它能帮助你回答:

  • 是否存在某个 routing key 的请求聚集
  • 当前运行中的 request cluster 是否偏斜
  • waiting queue 和 running batch 的 routing 分布是否一致

routed_dp_rankrouting_key 的边界#

它们很容易被混淆,但语义不同:

  • routed_dp_rank 更像“直接指定发到哪个 DP rank”
  • routing_key 更像“给调度与观测层一个聚类/亲和信号”

系统同时支持这两个字段,说明它承认“显式 placement 指令”和“软性分布信号”是不同层级的需求。

extract_routed_dp_rank_from_header(...) 为什么关键#

serving_base.py 明确允许从 header 提取 routed_dp_rank,并且优先级高于 body。这说明在真实部署中,路由系统可能并不在请求体里写 placement,而是通过更外层网关或代理注入 header。

这非常值得技术书强调,因为它说明 routing signal 的来源不一定在业务 payload,而可能来自更外层控制平面。

DataParallelController/v1/loads 为什么应该对照看#

前者决定请求被送到哪里,后者则帮助你验证“它现在到底被送到了哪里、各 rank 是否平衡”。如果你只看 controller 逻辑,不看 loads,很容易误以为策略应该没问题;如果你只看 loads,不看 controller,你又很难解释为什么会出现当前分布。

这就是为什么本章要把两者放在一起。

这一层最容易出现的误判#

1. 把 /v1/loads 当成“Prometheus 的另一个壳”#

它更像 scheduler 内部状态的结构化快照接口。

2. 把 routing key 当成业务字段,忽略其调度意义#

实际上系统会显式统计它在 running/waiting 中的分布。

3. 把 routed_dp_rank 当成 load balancing 结果#

很多时候它是外部直接指定的 placement,不是内部策略推导的。

如果你在排查“为什么请求总是去错地方”,先怎么查#

建议按这个顺序:

  1. 看请求是否带了 routed_dp_rank,来源在 header 还是 body。
  2. 看是否带了 routing_key
  3. DataParallelController 当前采用什么 LoadBalanceMethod
  4. /v1/loads 和 related metrics 看各 DP rank、各队列、各 routing key 的分布。
  5. 最后再决定问题出在外部 router、controller 策略,还是 scheduler 阶段的二次排序。

小结#

这一章真正想补齐的,是维护层里经常被忽略的一类“放置证据”:

  • /v1/loads 讲的是当前状态快照
  • routing_key 讲的是请求聚类与调度信号
  • routed_dp_rank 讲的是显式 placement 指令

到这里,扩展与调试部分就不仅会告诉你“系统快不快、活不活”,也开始告诉你“请求到底被放到了哪里,以及这种放置是否合理”。