Prhub

#23506 [gRPC] Native server: Rust crate (1/4)

原始 PR 作者 alexnails 合并时间 2026-05-19 13:31 文件变更 11 提交数 14 评论 28 代码增减 +2569 / -40

执行摘要

引入 Rust gRPC 服务器 crate,原生服务器第一阶段

SGLang 当前通过一个 gRPC 代理层提供服务,增加了不必要的延迟和复杂度。该 PR(与关联 Issue #22907 一致)旨在实现进程内原生 gRPC 服务器,提供更直接的通信路径和更好的性能。此 PR 是拆分 PR 栈的第一部分,专门负责 Rust crate 本身。

值得精读,特别是对 Rust ↔ Python 桥接模式感兴趣的人员。设计决策如 RequestAbortGuard Drop 语义、pyerr_to_status 分类、resolve_max_message_size 环境变量覆盖,都是良好的工程实践。未解决的 meta_info 序列化和认证问题应在后续 PR 中优先修复。

讨论亮点

Review 讨论涉及多个关键设计决策和技术债务:

  • 信号量必要性:ishandhanani 质疑 bridge.rs 中信号量的必要性,认为调度器已有流量控制参数。alexnails 最终移除了信号量,采用直通调度器控制。
  • 错误映射:JustinTong0323 要求将验证错误映射为 INVALID_ARGUMENT 而非 INTERNAL,保持与 HTTP 400 一致;alexnails 在后续提交中添加了 pyerr_to_status
  • 消息大小限制:JustinTong0323 指出需要显式配置 max message size;alexnails 添加了 DEFAULT_GRPC_MAX_MESSAGE_SIZE 64 MiB 和 SGLANG_TONIC_PAYLOAD 环境变量覆盖。
  • meta_info 序列化问题:ishandhanani 指出当前使用 v.str() 对嵌套 dict 会输出 Python repr 而非 JSON,导致客户端解析异常。推荐方案包括使用 json.dumps、typed proto 或 bytes 字段。当前未修复(技术债务)。
  • 认证缺失:JustinTong0323 强调 gRPC 服务缺少与 HTTP 一致的 API 密钥检查;ishandhanani 认为可在后续 PR 处理,不在 1/4 阻塞。
  • 构建方式:ispobock 质疑独立 maturin wheel 的用法,原始 #22736 采用嵌入式构建;alexnails 同意移除了独立 wheel,回归嵌入式构建。
  • 序列化开销:ishandhanani 注意到 proto → JSON → Python dict 的双重序列化开销,记录为优化点。

实现拆解

  1. 创建 Rust crate 与模块入口(lib.rs):定义 GrpcServerHandlestart_server Python 函数。新增 extract_tokenizer_info 从 Python RuntimeHandle 提取 tokenizer 配置。将占位实现替换为完整的服务器启动流程:创建 Tokio 运行时、绑定 TcpListener、应用 SglangServiceImpl。

  2. 实现 Python ↔ Rust 请求桥接(bridge.rs)PyBridge 持有 RuntimeHandle 的 PyObject 引用和带互斥锁的 BridgeState,管理按请求 ID 索引的 mpsc 通道。ResponseChunk 枚举表示三种片段,TerminalError 分类不可恢复错误。通过 make_chunk_callback 创建回调闭包,允许 Python 侧推送响应片段。

  3. 实现 gRPC 服务处理器(server.rs):定义 SglangServiceImpl,包装 PyBridge 和响应超时。实现 tonic 服务 trait,覆盖 TextGenerate、Embed、Classify 等 RPC。pyerr_to_status 将 Python 错误映射为合适的 gRPC Status(客户端错误 → INVALID_ARGUMENT,其余 → INTERNAL)。RequestAbortGuard 是一个 Drop 守卫,确保流丢弃时自动传播取消事件。通过 resolve_max_message_size 读取 SGLANG_TONIC_PAYLOAD 环境变量,使消息大小限制可配置。

  4. 集成 Rust 原生分词器(tokenizers.rs):定义 TokenizerBackend trait,实现 HuggingFaceTokenizerBackendRustTokenizer::from_tokenizer_path 在 tokenizer.json 存在且非 slow 模式时加载,否则静默回退 Python。

  5. 请求序列化与工具函数(utils/request_utils.rs)sampling_params_to_map 等函数将 proto 结构转为 serde_json Value,供 Python 侧消费。

  6. 测试覆盖(server/tests.rs、bridge/tests.rs 等):验证错误映射、环境变量覆盖、JSON 编码回退等核心行为。

文件 模块 状态 重要度
rust/sglang-grpc/src/server.rs gRPC 服务 added 9.18
rust/sglang-grpc/src/bridge.rs 桥接层 added 9.18
rust/sglang-grpc/src/tokenizers.rs 分词器 added 8.85
rust/sglang-grpc/src/lib.rs 模块入口 modified 8.84
rust/sglang-grpc/src/utils/request_utils.rs 请求工具 added 8.75
rust/sglang-grpc/src/server/tests.rs 服务测试 added 8.33

关键符号

start_server SglangServiceImpl::new SglangServiceImpl::text_generate PyBridge::new PyBridge::create_channel PyBridge::submit_generate resolve_max_message_size pyerr_to_status RequestAbortGuard::new RustTokenizer::from_tokenizer_path

分析完成后,这里会展示 LLM 生成的相对完整源码片段和详细注释。

评论区精华

桥接层信号量必要性讨论 设计

ishandhanani 询问为什么需要信号量,认为调度器已有 max_total_num_tokens 等参数;alexnails 表示不确信当前实现

结论:移除信号量,直通调度器控制 · 已解决

错误映射:验证错误应返回 INVALID_ARGUMENT 正确性

JustinTong0323 要求将验证错误映射为 INVALID_ARGUMENT 而非 INTERNAL,与 HTTP 400 一致

结论:添加 pyerr_to_status 函数,PyValueError/PyTypeError → INVALID_ARGUMENT,其他 → INTERNAL · 已解决

消息大小限制配置 性能

JustinTong0323 指出默认无显式限制,多模态等可能超出;要求配置

结论:添加 DEFAULT_GRPC_MAX_MESSAGE_SIZE 64 MiB 和 SGLANG_TONIC_PAYLOAD 环境变量覆盖 · 已解决

meta_info 序列化非 JSON 正确性

ishandhanani 指出 v.str() 对嵌套 dict 输出 Python repr,客户端需自解析;推荐 json.dumps、typed proto 或 bytes

结论:暂不修复,记录为技术债务 · unresolved

gRPC 服务缺少 API 认证 安全

JustinTong0323 要求 gRPC 端点与 HTTP 共享 auth 或启用时检查;ishandhanani 认为可在后续 PR 处理

结论:承认风险,后续 PR 修复 · unresolved

风险与影响

  1. 桥接层锁争用PyBridge 使用 Arc<Mutex<BridgeState>> 管理状态,高并发下多线程竞争可能成为瓶颈(ishandhanani 曾质疑)。
  2. meta_info 序列化不一致:当前 v.str() 输出 Python repr 而非 JSON,客户端(如 sgl-router)需要额外解析套路,破坏稳定性。
  3. 默认消息大小 64MiB:虽然可通过 SGLANG_TONIC_PAYLOAD 覆盖,但在某些场景(如大图片嵌入)仍可能不足,且无合理上限检查。
  4. 缺少认证:gRPC 端点暴露无防护,如果未经 auth 层包装即上线,可能被未授权访问。
  5. 请求序列化额外开销:proto → JSON → Python dict → GenerateReqInput 路径增加每次请求的 CPU 开销,高频请求下可能影响吞吐。
  6. 分词器静默回退:RustTokenize 加载失败时静默回退 Python,可能掩盖 tokenizer.json 损坏等错误,导致难以调试。

对用户无直接影响,因为此 PR 仅为 crate,不改变默认运行行为(需后续 PR 集成)。对系统而言,增加了 Rust 编译依赖(cargo、protoc)和运行时 Tokio 线程池;构建时间略微增加。对团队,需维护全新的 Rust 代码模块,但提供清晰的模块边界(bridge、server、tokenizers、utils)。长期看,该 crate 将为原生 gRPC 服务器奠定性能优化基础。

缺少 API 认证 元数据序列化非 JSON 桥接层潜在锁竞争 默认消息大小 64MiB 请求序列化额外开销 分词器静默回退可能掩盖错误

关联 Issue

#22907 [gRPC] Native gRPC server implementation and phase 1 fixes

完整报告

参与讨论