Prhub

#2048 Rename PortAllocator internals and pass the actor positionally

原始 PR 作者 fzyzcjy 合并时间 2026-09-04 13:49 文件变更 5 提交数 1 评论 1 代码增减 +22 / -20

执行摘要

重构 PortAllocator:重命名内部状态并调整 alloc 方法签名,提升可读性。

本 PR 是跟踪 Issue #1837 的一部分,旨在进行一系列重构与增强。具体动机是提升 PortAllocator 内部实现的语义清晰度和代码一致性。将 _values 重命名为 _next_port_of_ip 能更直观地表达其存储每个节点下一个可用端口的功能。将 alloc 方法的 engine 关键字参数改为位置参数 actor,使其在 functools.partial 绑定时更符合 Python 的习惯用法,也更清晰地表达了该参数是一个 Ray Actor 对象。

此 PR 是代码清洁度和可维护性的一次有价值改进,虽然变更本身简单,但体现了严谨的工程实践。它适合作为重构范例来学习。建议开发人员在执行类似重构时,可以参考此 PR 的做法:清晰命名、调整接口使其更自然、并确保所有测试同步更新。

讨论亮点

Review 评论中,审核者 guapisolo 表示这是在堆栈上下文中审查的,并指出后续的 PR #2053 会移除此处引入的临时 spec_session_server -> router_manager 反向依赖。该评论确认了本 PR 中的导入调整是临时性的,并且相关的修复(指 PR #2053)会在合并检查点 #2176 之前落地,且在审查的提交 tip #2649 时依然存在。这表明团队对依赖管理有清晰的计划和跟踪。

实现拆解

  1. 核心类 PortAllocator 重构:在 miles/utils/workers/addr_allocator.py 中,将实例变量 _values 重命名为 _next_port_of_ip,并更新了所有内部访问。同时,修改了 alloc 方法的签名,将关键字参数 engine 移动到位置参数 actor,并更新方法体内的变量名。此改动作为端口分配的核心逻辑单元,其接口变化直接影响所有调用方。
  2. 调用方适配:在 miles/ray/rollout/server_cell.py 的 start_engines 方法中,更新了对 port_allocator.alloc 的 functools.partial 调用,以匹配新的位置参数 actor。这确保了端口分配流程的延续性。
  3. 依赖导入调整:在 miles/ray/specs/inference.py 的 spec_session_server 函数中,新增了一个从 miles.ray.rollout.router_manager 导入 compute_num_session_server_ports 的本地导入。这是一个独立的依赖清理,与主重构逻辑关联较弱。
  4. 测试全覆盖同步:同步更新了 tests/fast/utils/workers/test_addr_allocator.py 和 tests/fast/ray/rollout/test_addr_allocator.py 中的所有测试用例,将内部状态断言从 cursors._values 改为 cursors._next_port_of_ip,并将 alloc 调用从关键字参数 engine=engine 改为位置参数 engine(注意测试中的变量名仍为 engine)。这确保了重构后的代码在单元测试和集成测试层面都保持正确性。
文件 模块 状态 重要度
miles/utils/workers/addr_allocator.py 端口分配器 modified 6.49
miles/ray/rollout/server_cell.py Rollout 服务单元 modified 4.49
miles/ray/specs/inference.py 推理规格 modified 4.09
tests/fast/utils/workers/test_addr_allocator.py 端口分配器测试 modified 4.5
tests/fast/ray/rollout/test_addr_allocator.py Rollout 端口分配测试 modified 3.55

关键符号

PortAllocator.alloc server_cell.start_engines inference.spec_session_server

关键源码片段

miles/utils/workers/addr_allocator.py core-logic

核心重构目标,定义了端口分配器的接口和内部状态。

import logging
from dataclasses import dataclass, field
​
import ray
​
logger = logging.getLogger(__name__)
​
​
@dataclass
class PortAllocator:
    # 内部状态:记录每个节点 IP 下一个可用的端口号。
    _next_port_of_ip: dict[str, int] = field(default_factory=dict)
​
    def alloc(self, actor, *, node_ip: str, consecutive: int = 1) -> int:
        """为指定节点分配端口。
​
        Args:
            actor: 一个 Ray Actor,用于实际探测空闲端口。将其作为位置参数传递,
                   使 API 更自然。
            node_ip: 节点 IP 地址。
            consecutive: 连续分配的端口数量。
​
        Returns:
            分配的起始端口号。
        """
        # 注释:使用小端口以避免 32768-65536 的临时端口范围。
        # 同时,Ray 使用 10002-19999,因此我们避开 10002 附近以避免竞争。
        start_port = self._next_port_of_ip.get(node_ip, 15000)
        port: int = ray.get(
            actor._get_free_port_block.remote(
                start_port=start_port,
                count=consecutive,
            )
        )
        self._next_port_of_ip[node_ip] = port + consecutive
        return port

评论区精华

临时导入依赖的生命周期 设计

审核者指出,在 `miles/ray/specs/inference.py` 中引入的 `from miles.ray.rollout.router_manager import compute_num_session_server_ports` 是临时性的反向依赖。

结论:此临时导入是更大重构序列的一部分,将在后续 PR #2053 中被移除,确保了依赖关系的最终清晰化。 · 已解决

风险与影响

风险极低。这是一个纯粹的命名和参数顺序重构,不改变任何业务逻辑或端口分配算法。所有变更都伴随着相应的测试更新,确保了行为的一致性。主要风险点在于对 PortAllocator.alloc 签名的修改,可能影响到未包含在此 PR 中的其他调用方。但鉴于测试覆盖全面,且回归风险已通过现有测试套件得到控制,此风险可忽略。

对用户无直接影响。对系统而言,它提升了内部代码的可维护性和清晰度,为未来可能的进一步重构(例如,使 PortAllocator 与具体的 Ray Actor 解耦)铺平了道路。对团队而言,它展示了在大型代码库中执行细致、可追踪重构的良好实践,并强调了测试在重构过程中的关键作用。

接口签名变更(潜在影响未列出的调用方)

关联 Issue

#1837 Tracking issue for refactoring and enhancements

完整报告

参与讨论