Prhub

#28573 support MPServer and embedded server for granian to enable muti tokenizer worker

原始 PR 作者 rainj-me 合并时间 2026-06-19 08:59 文件变更 5 提交数 6 评论 12 代码增减 +136 / -133

执行摘要

HTTP/2 支持多 tokenizer worker

此前 --enable-http2--tokenizer-worker-num > 1 无法同时使用(server_args.py 中会抛出 ValueError)。该 PR 旨在移除这一限制,让用户在启用 HTTP/2 时也能使用多个 tokenizer worker 实例以提升吞吐。

该 PR 值得精读,尤其是 _run_granian_server 的两种模式设计、以及如何通过移除旧环境变量简化代码。设计决策关注:嵌入式 vs 多进程服务器在不同 tokenizer worker 数下的选择逻辑。

讨论亮点
  • SSL 参数映射问题(评论者:merrymercy):指出 ssl_client_verify=ssl_verify 用法错误。ssl_verify 是用于 requests.verify 的值(True/False/CA 路径),而 Granian 的 ssl_client_verify 是服务器端布尔值,用于客户端证书验证。直接传入可能导致类型错误或意外启用双向 TLS。建议默认设为 False,除非显式添加 mTLS 选项。
  • 测试覆盖要求(评论者:ispobock):询问是否测试了 multi tokenizer 路径。作者 rainj-me 回复已添加对应测试。

实现拆解

  1. 重构 Granian 启动入口:在 python/sglang/srt/entrypoints/http_server.py 中重写 _run_granian_server 函数,当 tokenizer_worker_num == 1 时,使用 Granian 的嵌入式模式将 ASGI app 作为 asyncio task 运行在当前进程内,直接复用全局状态;当 tokenizer_worker_num > 1 时,使用 Granian 的 MPServer 启动多个 worker 进程,每个 worker 通过共享内存读取参数并调用 init_multi_tokenizer 初始化自己的 TokenizerManager。
  2. 移除旧的环境变量与初始化路径:删除 python/sglang/srt/environ.py 中的 SGLANG_GRANIAN_PARENT_PID 配置,删除 http_server.py 中的 _init_granian_worker 函数和 _close_main_process_sockets 函数,简化 get_main_process_id 逻辑(multi_tokenizer_mixin.py)。
  3. 解除 server_args 中的组合限制:在 python/sglang/srt/server_args.py_handle_ssl_validation 中移除对 --enable-http2--tokenizer-worker-num > 1 同时使用的 ValueError 校验。
  4. 新增测试覆盖:在 test/registered/openai_server/basic/test_http2_server.py 中增加 TestHTTP2ServerMultiTokenizer 类,使用 --enable-http2 --tokenizer-worker-num 2 启动服务,并继承基础测试用例,验证多 tokenizer 路径的请求处理。同时调整 CI 注册的预估时间。
文件 模块 状态 重要度
python/sglang/srt/entrypoints/http_server.py HTTP 服务 modified 8.81
python/sglang/srt/server_args.py 配置层 modified 5.75
test/registered/openai_server/basic/test_http2_server.py 集成测试 modified 5.99
python/sglang/srt/managers/multi_tokenizer_mixin.py Token 管理 modified 5.31
python/sglang/srt/environ.py 环境配置 modified 4.19

关键符号

_run_granian_server serve lifespan init_multi_tokenizer get_main_process_id

关键源码片段

python/sglang/srt/entrypoints/http_server.py dependency-wiring

核心变更文件,重写 Granian 启动函数(`_run_granian_server`),添加两种运行模式;移除旧的初始化逻辑(`_init_granian_worker`, `_close_main_process_sockets`);调整 `lifespan` 控制流。

# python/sglang/srt/entrypoints/http_server.py (head)
# lifespan 函数中移除旧分支,统一走 init_multi_tokenizer
@asynccontextmanager
async def lifespan(fast_api_app: FastAPI):
    if getattr(fast_api_app, "is_single_tokenizer_mode", False):
        server_args = fast_api_app.server_args
        warmup_thread_kwargs = fast_api_app.warmup_thread_kwargs
        thread_label = "Tokenizer"
    else:
        # 不再检查 envs.SGLANG_GRANIAN_PARENT_PID,所有 worker 进程
        # 都通过共享内存的 init_multi_tokenizer 初始化自身状态
        server_args = await init_multi_tokenizer()
        warmup_thread_kwargs = dict(server_args=server_args)
        thread_label = f"MultiTokenizer-{_global_state.tokenizer_manager.worker_id}"
# _run_granian_server 新签名的简要示意(具体实现因不完整未展开)
def _run_granian_server(
    host, port, log_level, tokenizer_worker_num=1,
    ssl_certfile=None, ssl_keyfile=None, ssl_ca_certs=None,
    ssl_keyfile_password=None, ssl_verify=False,
    backlog=2048, backpressure=2048,
):
    """
    当 tokenizer_worker_num == 1 时使用 Granian 嵌入式模式(当前进程内 asyncio task);
    当 tokenizer_worker_num > 1 时使用 Granian MPServer 启动多个 worker 进程。
    """
    # ...
python/sglang/srt/server_args.py core-logic

移除 `--enable-http2` 与 `--tokenizer-worker-num > 1` 不兼容的校验,是功能解锁的关键配置变更。

# python/sglang/srt/server_args.py (head)
# 在 _handle_ssl_validation 中,原代码抛出错误的部分已删除
if self.enable_http2:
    # ... granian 检查和 ssl_refresh 检查保持不变
    # 下方删除的 6 行语句:
    # if self.tokenizer_worker_num > 1:
    # raise ValueError(
    # "--enable-http2 does not yet support --tokenizer-worker-num > 1. "
    # "Multi-worker HTTP/2 support will be added in a future release."
    # )

评论区精华

SSL 参数传递错误 正确性

merrymercy 指出 `ssl_client_verify=ssl_verify` 用法错误:`ssl_verify` 是 `requests.verify` 的参数(True/False/CA 路径),而 Granian 的 `ssl_client_verify` 需要布尔值。可能导致类型错误或意外启用 mTLS。

结论:应改为 `False` 或添加独立选项。未在合并前修复。 · unresolved

Multi tokenizer 测试覆盖 测试

ispobock 询问是否测试了 multi tokenizer 路径。rainj-me 回复已添加。

结论:新增 TestHTTP2ServerMultiTokenizer 测试类覆盖此路径。 · 已解决

风险与影响

  • SSL 配置风险http_server.pyssl_client_verify=ssl_verify 的参数传递可能引发类型错误或意外启用 mTLS,导致正常请求失败(该问题在合并前未被修复)。
  • 嵌入式服务器稳定性:嵌入式模式直接在主 asyncio 事件循环中运行 server,若有阻塞操作将影响整体请求处理,需要保证所有处理为异步协程。
  • 回归风险:重构 Granian 启动流程、删除旧函数和环境变量,可能影响依赖 SGLANG_GRANIAN_PARENT_PID_init_granian_worker 的第三方集成。
  • 用户影响:现在可以同时使用 HTTP/2 与多个 tokenizer worker,提升高并发场景下的吞吐能力。单 worker 用户无感知。
  • 系统影响:嵌入式模式避免了额外的进程间通信开销,可能轻微改善延迟和 CPU 使用。
  • 团队影响:代码量大致持平(+136/-133),但移除了旧路径,降低了维护负担。
SSL 参数错误 嵌入式服务阻塞风险 旧环境变量移除兼容性

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论