执行摘要
- 一句话:修复 dp-aware 路由下 abort 无法触达引擎
- 推荐动作:值得精读。PR 展示了一个典型的路由身份与可寻址地址混淆的排查过程:从失败日志反推 URL 解析行为,对照 router 内部
base_url() 语义,最后用一个小而精确的工具函数统一修复。关注 router_worker_base_urls 的「仅剥离数字后缀」设计与去重逻辑,可作为后续库内地址归一化基础设施的参考。
功能与动机
PR body 指出 stage-c-2-gpu-h200 上 test_glm5_744b_a40b_4layer_r3.py 持续失败:引擎健康,但 abort() 无法触达,日志显示 All connection attempts failed 并耗尽 60 次重试。根因是 dp-aware 路由下 GET /workers 返回的 url 形如 http://worker1:8080@0,miles 直接拼接 f'{url}/abort_request' 后,httpx 将 host:port 解析为 userinfo、把 0 解析为主机名,所有连接必然失败。该问题由 #2012 开启 dp-aware 路由后暴露;作者明确表示 dp-aware 路由应该保留,需要更新的是消费方。
实现拆解
- 定位问题根源:dp-aware 路由下,
/workers 返回的 url 字段是如 http://host:port@<rank> 的路由身份标识,并非可寻址的 HTTP base。miles 此前直接把它拼接到 /abort_request,导致 httpx 将 host:port 解析为 userinfo、<rank> 解析为 hostname,所有连接必然失败。
- 新增统一工具函数:在
miles/utils/http_utils.py 中新增 router_worker_base_urls(),使用 rpartition('@') 剥离数字后缀(仅当后缀为纯数字时),并按首次出现顺序去重,确保同一引擎的多个 rank 只保留一个 base URL。
- 接入三处消费点:
miles/rollout/inference_rollout_train.py::get_worker_urls、miles/rollout/sglang_rollout.py::abort、miles/ray/multi_lora/backend.py::worker_urls 在返回前统一经过 router_worker_base_urls() 处理。其中 get_worker_urls 同时服务 abort() 与 dumper_utils.configure_sglang,后者此前在 dp-attention 运行中会同样失败。
- 补充测试:在
tests/fast/backends/sglang_utils/test_router_dp_aware.py 中新增 4 个用例,覆盖 rank 后缀剥离与去重、普通 URL 透传、IPv6 地址、非数字后缀保留。pre-commit run --all-files 通过。
- 验证:真实回归依赖
stage-c-2-gpu-h200,该测试使用 --sglang-enable-dp-attention --sglang-dp-size 2,可端到端覆盖 dp-aware 路径。
关键文件:
miles/utils/http_utils.py(模块 网络工具;类别 source;类型 core-logic;符号 router_worker_base_urls): 新增核心工具函数 router_worker_base_urls(),剥离 dp-aware 路由附加的 @ 后缀并去重,是本次修复的核心逻辑。
tests/fast/backends/sglang_utils/test_router_dp_aware.py(模块 DP 路由;类别 test;类型 test-coverage;符号 test_dp_rank_suffix_stripped_and_engines_deduplicated, test_plain_worker_urls_pass_through_in_order, test_ipv6_dp_rank_suffix_stripped, test_non_numeric_suffix_is_not_a_dp_rank): 为 router_worker_base_urls 补充 4 个单元测试,覆盖 rank 剥离与去重、普通 URL 透传、IPv6、非数字后缀保留,防止回归。
miles/rollout/inference_rollout/inference_rollout_train.py(模块 生成链路;类别 source;类型 dependency-wiring;符号 get_worker_urls): get_worker_urls 是实际触发线上失败的第一处消费点,同时服务 abort() 与 dumper 配置;修正后 dp-attention 运行可正常 abort。
miles/rollout/sglang_rollout.py(模块 生成链路;类别 source;类型 dependency-wiring;符号 abort): legacy 栈的 abort 逻辑与 inference_rollout_train 重复,需同步修正,避免两套路径行为不一致。
miles/ray/multi_lora/backend.py(模块 多 LoRA;类别 source;类型 dependency-wiring;符号 worker_urls, abort_adapter_requests): multi-LoRA 的 adapter abort 路径同样依赖 worker 列表作为 HTTP base,需一并修复,否则 dp-attention 下 adapter abort 也会失败。
关键符号:router_worker_base_urls, get_worker_urls, abort, worker_urls, abort_adapter_requests
关键源码片段
miles/utils/http_utils.py
新增核心工具函数 router_worker_base_urls(),剥离 dp-aware 路由附加的 @ 后缀并去重,是本次修复的核心逻辑。
def router_worker_base_urls(urls: list[str]) -> list[str]:
# 剥离 dp-aware 路由附加的 @<rank> 后缀;同一引擎的各 rank 收敛为同一地址
bases = []
for url in urls:
base, sep, rank = url.rpartition('@')
# 仅当后缀为纯数字时才认定为 DP rank;否则视为 userinfo,保留原样
if sep and rank.isdigit():
url = base
# 按首次出现顺序去重,避免对同一引擎重复发 abort_all
if url not in bases:
bases.append(url)
return bases
tests/fast/backends/sglang_utils/test_router_dp_aware.py
为 router_worker_base_urls 补充 4 个单元测试,覆盖 rank 剥离与去重、普通 URL 透传、IPv6、非数字后缀保留,防止回归。
from miles.utils.http_utils import router_worker_base_urls
def test_dp_rank_suffix_stripped_and_engines_deduplicated():
# dp-aware 路由为每个 DP rank 上报一条,但共享同一可寻址服务器
assert router_worker_base_urls(['http://h:8080@0', 'http://h:8080@1']) == ['http://h:8080']
def test_plain_worker_urls_pass_through_in_order():
# 普通 URL 不含 @rank 后缀,应原样透传且保持顺序
assert router_worker_base_urls(['http://h:8080', 'http://h:8081']) == ['http://h:8080', 'http://h:8081']
def test_ipv6_dp_rank_suffix_stripped():
# IPv6 地址带方括号,同样支持剥离 @rank 后缀
assert router_worker_base_urls(['http://[::1]:8080@0']) == ['http://[::1]:8080']
def test_non_numeric_suffix_is_not_a_dp_rank():
# 非数字后缀是 userinfo 而非 rank:剥离会改变实际寻址的主机
assert router_worker_base_urls(['http://user:pass@h:8080']) == ['http://user:pass@h:8080']
评论区精华
审核人 guapisolo 直接批准(LGTM.),没有进一步讨论。唯一外部评论来自 gemini-code-assist[bot] 的停用提醒,无实质技术内容。本 PR 的技术讨论集中在 PR body 中对根因的详细推导:作者用失败日志、sglang-router 的 url() 与 base_url() 语义对比,以及 httpx 对 http://host:port@rank 的解析行为,完整说明了「路由身份≠地址」这一核心矛盾,并据此决定在消费方统一做 base URL 归一化,而不是关闭 dp-aware 路由。
风险与影响
- 风险:行为变更影响所有经 router worker 条目发起的引擎级请求(
abort_request、dumper/configure、adapter abort),都会去掉 @rank 后缀;若未来需要定向请求某个 DP rank,则需要新机制。剥离逻辑依赖数字后缀约定,若 sglang-router 改变格式(如 @rank-0),isdigit() 检查会使 URL 原样透传,可能再次导致连接失败但不会误伤 userinfo。仅覆盖已知三处消费点,其他将 worker['url'] 直接拼 URL 的代码可能遗漏(但 git log -S router_dp_aware 仅一个 commit,概率低)。单测覆盖工具函数,真实回归依赖 stage-c-2-gpu-h200,缺少独立端到端 CI 用例。
- 影响:修复 dp-attention + dp-aware 路由下 rollout
abort() 与 dumper 配置失败的严重问题,避免整轮训练崩溃。影响范围:使用 --sglang-enable-dp-attention 的推理/训练任务(如 GLM5 744B 等),以及 multi-LoRA 场景下的 adapter abort 路径。对非 dp-aware 路径无影响,普通 URL 原样透传。维护方面,库中新增一个可复用的地址归一化工具,后续同类集成应复用。
- 风险标记:路由身份与地址混用, 仅覆盖已知消费点, 依赖 router 后缀格式
关联脉络
- PR #2012 router: enable dp-aware routing under dp-attention: 本 PR 是 #2012 的后续修复:#2012 开启 dp-aware 路由后,暴露了 miles 将路由身份标识当作 HTTP base 使用的问题。
参与讨论