Prhub

#35239 Rainj me/rust server refactor2

原始 PR 作者 rainj-me 合并时间 2026-08-22 07:37 文件变更 61 提交数 20 评论 6 代码增减 +2537 / -2199

执行摘要

重构 Rust Server 模块,配置改为强类型传递

PR body 明确写着 “Refactor the rust server by addressing comments.”,且 commit 历史中多次出现 “address the refactor comments”“remove opaque server_args_json data type” 等提交,说明这是一次响应评审意见的迭代重构;目标是解决 rust server 代码集中在大文件、且配置以 JSON blob 传递而无法静态校验类型的问题。

值得精读。重点看 message/config.rs 的 pyo3 强类型配置模式——它把 Python↔Rust 边界从“字符串 blob + serde 解析”升级为“类型化构造 + 编译期校验”,是消除运行时错误的典型案例;同时可学习其模块拆分方式,理解 Rust server 后续演进结构。

讨论亮点

PR 没有 review 评论;6 条 issue 评论全部是 CI 触发与反馈。merrymercy 用 /rerun-test 指定重跑 test/registered/core/test_srt_endpoint.py、test/registered/vlm/test_rust_native_mm_e2e.py、test/registered/vlm/test_rust_native_mm_mmmu.py、test/registered/rust/test_run_rust_tests.py;github-actions 反馈全部通过。期间发现 test_cargo_workspace.py 已不存在,说明测试路径随重构而迁移。

实现拆解

  1. 模块重组与文件拆分:将 rust/sglang-server/src/message.rs 拆分为 message/{config,request,response,io_struct,ids}.rs 等;将 mm.rs 拆分到 multi_modality/{worker,sidecar,shm}.rs;将 runtime.rs 移动到 utils/runtime.rs;将 ring.rs 移动到 tokenizer_manager/channel.rs;将 api_server.rs 拆分为 api_server/{app,common,native_api}.rs。
  2. 配置传递类型化:删除 runtime/config.rs,新增 message/config.rs 中的 #[pyclass] ServerArgs/ModelConfig/RuntimeConfig/MmSpec;Python 侧 RustServer._build_server_args 改为关键字构造,Rust 侧只做 validate() 不再 from_json。
  3. 语义重命名:Ingress→Intake、ingress_ring→to_scheduler、egress_ring→from_scheduler、MmHandoff→MmEncodeResult 等,同步更新所有引用与注释。
  4. 配套测试与 CI:更新 test/registered/rust 等测试路径,修复硬编码 endpoint 源文件路径的用例,调整 workflow 中 cargo test 阶段位置。
  5. 收尾修正:最后一个 commit 修正 est_time 值,随后合并。
文件 模块 状态 重要度
rust/sglang-server/src/message/config.rs 配置层 added 9.08
rust/sglang-server/src/tokenizer_manager/to_scheduler.rs 请求入口 renamed 9.28
rust/sglang-server/src/lib.rs 边界层 modified 8.69
rust/sglang-server/src/utils/runtime.rs 运行时 renamed 8.78
python/sglang/srt/managers/rust_server.py 管理器 modified 8.3

关键符号

ServerArgs::py_new ServerArgs::validate Intake::run Runtime::start Runtime::spawn_mm_pool MmWorker::run sidecar::park_features_in_shm ToSchedulerTx::try_push RustServer._build_server_args RustServer._build_mm_spec

关键源码片段

rust/sglang-server/src/message/config.rs dependency-wiring

本次重构的核心:将 server_args 从 JSON blob 解析改为 pyo3 pyclass 强类型构造,并汇聚 RustServerServerArgs、RuntimeConfig、ModelConfig、MmSpec 等配置类型,是 Python↔Rust 边界的单一 schema。

//! Python 侧通过 `_server.ServerArgs(...)` 按关键字构造;
//! 每个字段都是必填且类型化,pyo3 在构造时即校验,漏传或错型直接抛异常。#[pyo3::pymethods]
impl ServerArgs {
    #[new]
    #[pyo3(signature = (*,
        model_path, served_model_name, tokenizer_path, revision, load_format,
        weight_version, host, port, log_level, log_level_http, chat_template,
        tool_call_parser, reasoning_parser, stream_response_default_include_usage,
        tokenizer_worker_num, detokenizer_worker_num, skip_tokenizer_init,
        incremental_streaming_output, disaggregation_mode, model_config,
        preferred_sampling_params, allow_auto_truncate, enable_return_hidden_states,
        num_reserved_tokens, version, max_total_num_tokens,
    ))]
    #[allow(clippy::too_many_arguments)]
    fn py_new(
        model_path: String,
        served_model_name: String,
        tokenizer_path: String,
        revision: Option<String>,
        load_format: Option<String>,
        weight_version: Option<String>,
        host: String,
        port: u16,
        log_level: String,
        log_level_http: Option<String>,
        chat_template: Option<String>,
        tool_call_parser: Option<String>,
        reasoning_parser: Option<String>,
        stream_response_default_include_usage: bool,
        tokenizer_worker_num: usize,
        detokenizer_worker_num: usize,
        skip_tokenizer_init: bool,
        incremental_streaming_output: bool,
        disaggregation_mode: DisaggregationMode,
        model_config: ModelConfig,
        preferred_sampling_params: Option<PreferredSamplingParams>,
        allow_auto_truncate: bool,
        enable_return_hidden_states: bool,
        num_reserved_tokens: u64,
        version: String,
        max_total_num_tokens: u64,
    ) -> Self {
        Self {
            model_path, served_model_name, tokenizer_path, revision, load_format,
            weight_version, host, port, log_level, log_level_http, chat_template,
            tool_call_parser, reasoning_parser, stream_response_default_include_usage,
            tokenizer_worker_num, detokenizer_worker_num, skip_tokenizer_init,
            incremental_streaming_output, disaggregation_mode, model_config,
            preferred_sampling_params, allow_auto_truncate, enable_return_hidden_states,
            num_reserved_tokens, version, max_total_num_tokens,
        }
    }
}
rust/sglang-server/src/tokenizer_manager/to_scheduler.rs rename-or-move

原 ingress.rs 重命名并改造为 Intake 状态机,是请求进入调度器的核心路径;同时 Limits 从 TryFrom 改为 From,channel 语义全面切换为 to_scheduler/from_scheduler。

/// Intake 是单消费者阶段:只消费一个 inbox(由 API server 与 tokenizer 池共同喂入),
/// 驱动请求状态机后把请求 *move* 到下一阶段;全程无共享,不需要锁。
impl Runnable for Intake {
    fn run(mut self) {
        loop {
            // 用 Select 而不是 drain-then-block:abort 在 inbox 空闲时到达也必须立即处理。
            let next = flume::Selector::new()
                .recv(&self.abort_rx, |r| r.ok().map(Lane::Abort))
                .recv(&self.tok_manager_rx, |r| r.ok().map(Lane::Event))
                .recv(&self.shutdown, |_| None)
                .wait();
            match next {
                Some(Lane::Abort(rid)) => self.on_abort(rid),
                Some(Lane::Event(TmEvent::Intake(req) | TmEvent::Tokenized(req))) => {
                    self.drive(req) // 新请求与 tokenizer 池返回的请求走同一条驱动路径
                }
                Some(Lane::Event(TmEvent::MmEncoded { rid, input_ids })) => {
                    self.on_mm_encoded(rid, input_ids)
                }
                Some(Lane::Event(TmEvent::MmFailed { rid, message })) => {
                    self.on_mm_failed(rid, message)
                }
                None => {
                    // 收到 shutdown,或 inbox 已关闭:先把 abort 队列中仍在途的请求处理完。
                    // selector 可能在看到 pending abort 之前就报告 inbox 关闭。
                    while let Ok(source) = self.abort_rx.try_recv() {
                        self.on_abort(source);
                    }
                    return;
                }
            }
        }
    }
}

评论区精华

CI 重跑范围与 Rust 测试路径 测试

merrymercy 用 /rerun-test 指定重跑 test/registered/core/test_srt_endpoint.py、test/registered/vlm/test_rust_native_mm_e2e.py、test/registered/vlm/test_rust_native_mm_mmmu.py、test/registered/rust/test_run_rust_tests.py;此前尝试的 test_cargo_workspace.py 已被重构移除。

结论:重跑全部通过,Rust 原生多模态端到端路径未回归。 · 已解决

风险与影响

  1. 核心路径变更:to_scheduler/from_scheduler 是请求进出调度器的唯一通道,重命名与 ring 结构调整影响面广。
  2. 大范围重命名:测试中仍存在硬编码源码路径的用例(本 PR 修复了其中一个),后续可能还有遗漏。
  3. 边界行为变化:server_args_json 过去允许未知 key 忽略,现在 pyo3 构造要求所有字段必填,任何字段漂移都会导致 boot 失败,属于有意 fail-fast 但行为改变。
  4. 测试覆盖有限:CI 只重跑了 srt_endpoint、rust native mm e2e 等少数测试,未覆盖全部多模态路径和 PD 场景。

对开发者:Rust server 目录结构成为新基准,后续 PR 需适配;对运行时:配置加载路径从运行期 JSON 解析变为编译期类型构造,boot 失败时机提前,错误信息更明确;对团队:CI workflow 中 cargo test 阶段调整,rust 测试路径变更。对外部用户无直接行为变化,公共 API 不变。影响程度属于基础设施级重构。

核心路径变更 大范围重命名 测试覆盖有限 边界行为变化

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论