# PR #2041 完整报告

- 仓库：`radixark/miles`
- 标题：Address engines by base URL when the router is dp-aware
- 合并时间：2026-08-01 08:10
- 原文链接：http://prhub.com.cn/radixark/miles/pull/2041

---

# 执行摘要

- 一句话：修复 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 路由应该保留，需要更新的是消费方。

# 实现拆解

1. 定位问题根源：dp-aware 路由下，`/workers` 返回的 `url` 字段是如 `http://host:port@<rank>` 的路由身份标识，并非可寻址的 HTTP base。miles 此前直接把它拼接到 `/abort_request`，导致 httpx 将 `host:port` 解析为 userinfo、`<rank>` 解析为 hostname，所有连接必然失败。
2. 新增统一工具函数：在 `miles/utils/http_utils.py` 中新增 `router_worker_base_urls()`，使用 `rpartition('@')` 剥离数字后缀（仅当后缀为纯数字时），并按首次出现顺序去重，确保同一引擎的多个 rank 只保留一个 base URL。
3. 接入三处消费点：`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 运行中会同样失败。
4. 补充测试：在 `tests/fast/backends/sglang_utils/test_router_dp_aware.py` 中新增 4 个用例，覆盖 rank 后缀剥离与去重、普通 URL 透传、IPv6 地址、非数字后缀保留。`pre-commit run --all-files` 通过。
5. 验证：真实回归依赖 `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 路由附加的 @<rank> 后缀并去重，是本次修复的核心逻辑。
- `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 路由附加的 @<rank> 后缀并去重，是本次修复的核心逻辑。

```python
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、非数字后缀保留，防止回归。

```python
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 使用的问题。