执行摘要
- 一句话:新增 ZMQ 套接字上限配置,修复 PD 大规模连接失败
- 推荐动作:建议运维和 disaggregation 相关开发阅读。该 PR 揭示了一个非直觉的规模瓶颈(libzmq per-context 上限而非 OS fd 上限),并以最小改动提供可调配置。值得关注的决策:默认值取 16384(约 16 倍默认值)并留在 Envs 中统一管理;同时作者明确承认 socket 缓存无界,后续 LRU eviction 是真正的根治方向,读者可跟踪该后续工作。
功能与动机
PR body 指出:Prefill ranks 可以耗尽 libzmq per-context socket cap(默认 1023),即使 OS 文件描述符限制足够。KV manager 为每个 decode endpoint 缓存一个 PUSH socket 和一个 PAIR monitor socket,因此 16 个 decode replicas × 32 DP ranks 需要 1025 个 sockets,transfer workers 在 connect 时开始失败。这是一个连接容量缓解措施。
实现拆解
1. 新增环境变量定义
在 python/sglang/srt/environ.py 的 Envs 类中,于 PD disaggregation 配置段新增 SGLANG_DISAGGREGATION_ZMQ_MAX_SOCKETS = EnvInt(16384),默认值 16384(约 libzmq 默认上限 1023 的 16 倍),供所有组件通过 envs...get() 读取。
2. KVManager 初始化时提升 context 上限
在 python/sglang/srt/disaggregation/common/conn.py 的 KVManager 初始化方法中,创建 self._zmq_ctx = zmq.Context() 后立即调用 self._zmq_ctx.set(zmq.MAX_SOCKETS, envs.SGLANG_DISAGGREGATION_ZMQ_MAX_SOCKETS.get()),使该 context 内缓存的 PUSH/PAIR 套接字不受默认限制。
3. CommonKVReceiver 类级 context 同步设置
对共享的 CommonKVReceiver._ctx 类属性(zmq.Context())同样调用 _ctx.set(zmq.MAX_SOCKETS, ...),确保复用接收端套接字时也享有新上限。
4. 测试与文档
没有新增测试或文档(PR body 明确说明),作者仅验证了语法编译;注释在 4 个 commit 中逐步精简。
关键文件:
python/sglang/srt/environ.py(模块 环境配置;类别 source;类型 configuration;符号 SGLANG_DISAGGREGATION_ZMQ_MAX_SOCKETS): 定义新环境变量 SGLANG_DISAGGREGATION_ZMQ_MAX_SOCKETS,作为配置入口,默认值 16384
python/sglang/srt/disaggregation/common/conn.py(模块 KV 传输;类别 source;类型 core-logic;符号 KVManager.init, CommonKVReceiver): 核心变更,两处应用 zmq.MAX_SOCKETS,解决大规模 PD 部署连接失败
关键符号:KVManager.init, CommonKVReceiver
关键源码片段
python/sglang/srt/disaggregation/common/conn.py
核心变更,两处应用 zmq.MAX_SOCKETS,解决大规模 PD 部署连接失败
# python/sglang/srt/disaggregation/common/conn.py
class KVManager:
def __init__(self, ...):
...
# bind zmq socket
self._zmq_ctx = zmq.Context()
# 将 per-context 套接字上限从 libzmq 默认 1023 提升为可配置值,
# 因为该 manager 为每个 decode endpoint 缓存 PUSH 与 PAIR monitor 两个 socket,
# 大规模部署(如 16 decode × 32 DP)会超过默认上限,导致 transfer worker 连接失败。
self._zmq_ctx.set(
zmq.MAX_SOCKETS, envs.SGLANG_DISAGGREGATION_ZMQ_MAX_SOCKETS.get()
)
self.rank_port, self.server_socket = get_zmq_socket_on_host(
self._zmq_ctx, zmq.PULL, host=self.local_ip
)
...
class CommonKVReceiver(BaseKVReceiver):
_ctx = zmq.Context()
# 共享接收端 context 同样应用新上限,保证所有复用 socket 不受 libzmq 默认 1023 约束
_ctx.set(zmq.MAX_SOCKETS, envs.SGLANG_DISAGGREGATION_ZMQ_MAX_SOCKETS.get())
_socket_cache = {}
...
评论区精华
PR 没有 review 评论。作者在 4 次提交中反复调整注释(从移除冗余注释到澄清动机),最终注释简洁说明“每个 decode endpoint 缓存 2 个 socket,大规模部署会超过默认上限”。Issue 评论仅有 /tag-and-rerun-ci 指令,无技术讨论。
风险与影响
- 风险:
- 文件描述符瓶颈转移:提升 ZMQ 上限后,OS 层面 ulimit -n 可能成为新的约束(ZMQ socket 底层也占用 fd),大规模部署仍需同步调高 fd 限制。
- 内存增长:socket 缓存仍然无界,作者在 commit 中承认这是 minimal mitigation,后续需 LRU 驱逐来回收不活跃 socket。
- 类级设置时机:
CommonKVReceiver._ctx.set(...) 在类定义时执行,若 pyzmq 导入失败会在导入阶段直接报错;不过 conn.py 本就强依赖 zmq,风险低。
- 缺少自动化测试:没有针对新环境变量的单元测试,但改动仅调整 context 选项,不改变消息语义,回归面小。
- 影响:
- 用户侧:PD 大规模部署(如 16 decode × 32 DP)不再因 libzmq 默认 socket 上限发生连接失败,可用性提升。
- 系统侧:ZMQ context 容量上限默认提高至 16384,内存占用与活跃连接数正相关;常规规模无感知。
- 团队侧:新增 SGLANG 前缀环境变量(专家级配置),建议在 PD 部署文档中补充说明;当前 PR 未同步文档。
- 影响范围:仅限 PD disaggregation 的 socket 创建路径(conn.py),不影响其他模块和模型输出。
- 风险标记:socket 缓存无界, 默认值依赖 OS fd 限制, 缺少测试覆盖, 类级 context 导入期设置
关联脉络
- PR #33807 [PD] Support pipeline-parallel prefill with Mooncake staging buffer: 同属 PD disaggregation 稳定性与功能增强,涉及 conn.py 等公共连接组件,本 PR 是对其大规模部署场景的补充缓解。
- PR #34341 [npu] [bugfix] Fix HiCache MHA backup for NPU: 同为 disaggregation 路径的 bugfix,表明 PD 拆分场景持续有可靠性修复,与本 PR 目标一致。
参与讨论