SGLang 推理框架代码结构与核心逻辑一、仓库整体结构sglang/ ├── python/sglang/ # 核心 Python 代码 │ ├── srt/ # SGLang Runtime推理引擎核心 │ └── ... # 前端 DSL / API 等 ├── rust/ # Rust 实现的高性能组件如 router ├── sgl-model-gateway/ # 模型网关服务 ├── 3rdparty/ # 第三方依赖如 sgl-kernel ├── benchmark/ # 性能测试 ├── test/ # 测试用例 ├── docker/ # Docker 部署 ├── docs/ # 文档/Cookbook └── examples/ # 使用示例二、核心推理引擎 (python/sglang/srt/)目录/文件功能entrypoints/HTTP/gRPC 入口兼容 OpenAI APImanagers/scheduler.py调度器核心中的核心管理 prefill/decode 批调度managers/tokenizer_manager.py接收请求、tokenize、分发给 schedulermodel_executor/模型前向推理执行器管理 CUDA graph、forward batchmodels/各模型实现Llama, DeepSeek, Qwen 等layers/模型层实现包括 attention、quantization、MoE 等mem_cache/KV Cache 管理RadixTree、内存池、缓存策略disaggregation/PD 分离Prefill-Decode 解耦架构speculative/投机解码distributed/分布式通信TP/PP/DP/EPsampling/采样策略三、推理逻辑主流程用户请求 → HTTP Server → TokenizerManager → Scheduler → ModelRunner → 返回 (顾客点菜) (前台接待) (服务员翻译菜单) (厨房调度) (厨师做菜) (上菜)3.0 进程结构SGLang 是多进程架构SGLang 启动时不是单一进程而是多进程 ZMQ 通信┌──────────────────────┐ ZMQ/IPC ┌──────────────────────┐ ZMQ/IPC ┌──────────────────┐ │ HTTP Server 进程 │ ────────→ │ Scheduler 进程 │ ────────→ │ Detokenizer 进程 │ │ (FastAPI │ │ (调度 ModelRunner) │ │ (还原文本) │ │ TokenizerManager) │ ←──────── │ 跑在 GPU 上 │ ←──────── │ │ └──────────────────────┘ └──────────────────────┘ └──────────────────┘TokenizerManager 与 HTTP Server同进程异步协程Scheduler 是独立进程占用 GPU启动代码entrypoints/engine.py的init_tokenizer_manager、run_scheduler_process、run_detokenizer_process设计目的HTTP 进程崩溃不影响 GPU 上的 Schedulertokenize 的 CPU 开销不阻塞 GPU。3.1 阶段一HTTP Server → TokenizerManager详细链路以帮我写一首关于春天的诗为例POST /v1/chat/completions { model: Qwen2.5-7B, messages: [{role: user, content: 帮我写一首关于春天的诗}], stream: true }经过的每一层#阶段做什么关键代码位置①Uvicorn收 TCP 字节流解 HTTP 协议ASGI 服务器非 SGLang 代码②FastAPI 路由匹配到openai_v1_chat_completionshttp_server.py:1702③validate_json_request校验Content-Type: application/jsonhttp_server.py:627④CORS / 解压中间件跨域头、可选的 gzip/br 解压http_server.py:460-473⑤Pydantic 反序列化JSON →ChatCompletionRequest对象字段类型校验entrypoints/openai/protocol.py⑥OpenAIServingChat.handle_request打时间戳、业务校验、日志、分流 stream/非 streamserving_base.py:73⑦_convert_to_internal_requestOpenAI 协议 → SGLang 内部协议serving_chat.py:903⑧_process_messages应用 chat template可能直接 tokenizeserving_chat.py:1029⑨to_sampling_params打包 temperature/top_p/max_tokens 等entrypoints/openai/protocol.py⑩构造GenerateReqInputSGLang 内部统一请求对象managers/io_struct.py⑪tokenizer_manager.generate_request(...)正式进入 TokenizerManagerserving_chat.py:1510 / 1720chat template 举例第 ⑧ 步[{role:user,content:帮我写一首关于春天的诗}] ↓ apply_chat_template |im_start|user\n帮我写一首关于春天的诗|im_end|\n|im_start|assistant\n ↓ tokenize可能在此步、也可能延后到 TokenizerManager [24212, 2170, 5765, 671, 7941, ...]关键点tokenize 有时在 HTTP 进程里就做完apply_chat_template(tokenizeTrue)有时把字符串传给 TokenizerManager 再 tokenize取决于是否多模态、chat_encoding_spec 等。3.2 阶段二TokenizerManager → SchedulerTokenizerManager(managers/tokenizer_manager.py)兜底 tokenize若上一步没做分配 request idrid通过 ZMQ 把GenerateReqInput发送给 Scheduler 进程用 asyncio Future 等待结果支持流式返回Scheduler(managers/scheduler.py)维护等待队列和运行批次running batch每次迭代几毫秒一次决策新请求 prefill / 老请求 decode / 淘汰 KV CacheContinuous Batching请求完成立即离开新请求立即插入管理 KV Cache 的分配与回收ModelRunner(model_executor/)Prefill一次性处理 prompt生成 KV CacheDecodebatch 中每个请求生成 1 个 tokenCUDA Graph 捕获重放减少 kernel launch 开销结果返回输出 token 通过 ZMQ 发给 Detokenizer 进程Detokenizer 还原文本回传给 TokenizerManagerTokenizerManager 通过event.set()唤醒等待协程经 SSE 流式返回给用户3.3 深入TokenizerManager 如何通过 ZMQ Event 与 Scheduler 通信通信架构两条独立通道发送和接收是独立的 ZMQ 管道不是请求-响应模式TokenizerManager 进程 Scheduler 进程 ┌────────────────────────┐ ┌──────────────────┐ │ send_to_scheduler │ ZMQ PUSH │ 接收请求 │ │ (PUSH socket) ─────────┼───────────────────→│ 执行推理 │ │ │ │ │ │ recv_from_detokenizer │ ZMQ PULL │ Detokenizer │ │ (PULL socket) ←────────┼────────────────────┼── 发回结果 │ └────────────────────────┘ └──────────────────┘请求和响应靠ridrequest id对应。核心流程三步发送 → 等待 → 唤醒步骤 A建立信箱 —_init_req_state(tokenizer_manager.py:3414)stateReqState(out_list[],# 结果队列后台循环往里塞数据finishedFalse,# 是否完成eventasyncio.Event(),# ★核心用于唤醒等待协程★objsub_obj,time_stats...,)self.rid_to_state[rid]state# 按 rid 注册到全局字典为什么用 Event 而不是 Future——一个请求流式产出多个 tokenFuture 只能 resolve 一次Event 可以反复set()/clear()。步骤 BZMQ 发送 —_send_one_request(tokenizer_manager.py:1540)def_send_one_request(self,tokenized_obj):tokenized_objwrap_shm_features(tokenized_obj)# 多模态大张量走共享内存tokenized_obj.wrap_pickle_fields()# 部分字段 pickleself._dispatch_to_scheduler(tokenized_obj)# sock_send → ZMQ PUSH非阻塞state.dispatchedTrue_dispatch_to_scheduler最终调用sock_send(self.send_to_scheduler, obj)扔进管道就返回。步骤 CEvent 等待 —_wait_one_response(tokenizer_manager.py:1662)asyncdef_wait_one_response(self,obj,requestNone):stateself.rid_to_state[obj.rid]whileTrue:awaitasyncio.wait_for(state.event.wait(),timeout5s)# 挂起等信号out_liststate.out_list# 取走所有已到达的结果state.out_list[]finishedstate.finished state.event.clear()# 清信号准备等下一批iffinished:yieldout;break# 完成结束ifis_stream:yieldout# 流式吐中间结果继续等event.wait()让协程挂起不占 CPU超时后检测客户端是否断连断了就 abort被唤醒后取走结果 → yield流式返回→ clear → 继续循环接收侧后台handle_loop如何唤醒auto_create_handle_loop启动一个后台协程死循环从 ZMQ 拉结果 (tokenizer_manager.py:2173)asyncdefhandle_loop(self):whileTrue:recv_objawaitasync_sock_recv(self.recv_from_detokenizer)# 阻塞收awaitself._handle_batch_output(recv_obj)# 分发_handle_batch_output遍历这批结果中的每个 rid找到ReqState塞结果并唤醒fori,ridinenumerate(recv_obj.rids):stateself.rid_to_state[rid]state.finishedrecv_obj.finished_reasons[i]isnotNonestate.out_list.append(out_dict)# ← 结果入队pending_notify[rid]stateforsinpending_notify.values():s.event.set()# ← 唤醒 _wait_one_response 协程完整时序图generate_request 协程 后台 handle_loop 协程 Scheduler 进程 │ │ │ A. _init_req_state │ │ 建 ReqState(event) │ │ │ │ │ B. _send_one_request │ │ ZMQ PUSH ───────────────────────────┼────────────────────────→│ 收到请求 │ │ │ prefill/decode C. await event.wait() ← 挂起 │ │ 生成token ┆ recv from ZMQ ←──────────────────│ Detokenizer发回 ┆ out_list.append(结果) │ ┆ event.set() ──┐ │ ┆ │ │ │ 被唤醒 ←────────────────────────────────────┘ │ 取 out_list, clear event │ │ yield out (流式返回) │ │ │ │ │ 继续 await event.wait()... │ │ ... 直到 finishedTrue 跳出 │设计要点设计原因ZMQ PUSH/PULL 而非请求-响应跨进程解耦Scheduler 可批量乱序处理靠 rid 对应Event out_list而非 Future流式多次返回Future 只能 resolve 一次独立的 handle_loop 后台协程一个 loop 服务所有并发请求的结果收取rid_to_state 字典异步结果靠 rid 路由到正确的等待协程batch_notify_size 批量唤醒减少 event.set() 和协程切换开销共享内存传大特征多模态张量不塞 ZMQ避免序列化拷贝wait_for 超时 is_disconnected检测客户端断连及时 abort 释放 GPU3.4 深入Scheduler 主循环Scheduler 是独立进程跑在 GPU 上主循环是同步的 while 死循环不是 asyncio每一轮做四件事收请求 → 排调度 → 跑 forward → 处理结果。主循环入口 —event_loop_normalscheduler.py:1682defevent_loop_normal(self):whileTrue:ifself.gracefully_exit:break# ① 收请求recv_reqsself.request_receiver.recv_requests()self.process_input_requests(recv_reqs)# ② 决定下一批跑什么planself.get_next_batch_to_run(running_batchself.running_batch,last_batchself.last_batch,)batchplan.batch_to_run# ③ 跑 forwardifbatch:resultself.run_batch(batch)self.process_batch_result(batch,result)# ④ 处理结果、送回 detokenizerelse:self.on_idle()self.last_batchbatch进阶版event_loop_overlap默认开启scheduler.py:1716。核心思想上一轮结果的 CPU 处理和这一轮的 GPU forward重叠执行defevent_loop_overlap(self):self.result_queuedeque()whileTrue:recv_reqsself.request_receiver.recv_requests()self.process_input_requests(recv_reqs)planself.get_next_batch_to_run(...)batchplan.batch_to_run# 先启动这一批的 GPU forward异步不等 GPUifbatch:batch_resultself.run_batch(batch)self.result_queue.append((batch.copy(),batch_result))# 然后 CPU 处理上一批的结果# 这两步就重叠了GPU 在算新的CPU 在整理旧的ifself.last_batch:tmp_batch,tmp_resultself.result_queue.popleft()self.process_batch_result(tmp_batch,tmp_result)self.last_batchbatch这就是 SGLang “Overlap Scheduler” 的本质用一步的延迟换 CPU/GPU 并行。四步详解① 收请求 —process_input_requests(scheduler.py:1838)从 ZMQ PULL socket 拉出 TokenizerManager 发来的TokenizedGenerateReqInput用_request_dispatcher分发生成请求 → 加入self.waiting_queue等待队列Abort / 控制命令 → 立即处理② 调度决策 —get_next_batch_to_run(scheduler.py:2925)调度大脑核心逻辑# 1. 处理超时/异常self._abort_on_waiting_timeout()self._abort_on_running_timeout(running_batch)# 2. 把上一批 prefill 完成的请求合并到 running_batchiflast_batchandlast_batch.forward_mode.is_extend():running_batch.merge_batch(last_batch)# 3. 尝试凑一批 prefill新请求prefill_planself.get_new_batch_prefill(running_batch)new_batchprefill_plan.batch_to_run# 4. 决策优先 prefill否则 decodeifnew_batchisnotNone:retnew_batch# 有新请求可以 prefillelse:ifnotrunning_batch.is_empty():running_batchself.update_running_batch(running_batch)retrunning_batch# 没新请求那就 decode 老的else:retNone# 完全空闲Prefill 优先原则新请求不 prefill 就不能开始生成会拖累 TTFT首 token 延迟。get_new_batch_prefill(scheduler.py:3067) 用PrefillAdder逐个尝试加入等待队列的请求前缀查找从tree_cacheRadixTree里找可复用的 KV 前缀只需为新增部分分配显存显存预算估算这批 prefill 需要多少 KV token超预算就停Chunked Prefill超长 prompt 切成 chunk 分几步 prefill避免一次 forward 拖累 decode 延迟update_running_batchdecode 前的准备淘汰已经finished的请求生成 EOS 或达到 max_tokens必要时 retract显存快满时把某些请求暂时踢回等待队列释放 KV Cache为每个存活请求分配下一个 token 的 KV slot③ 跑 forward —run_batch(scheduler.py:3528)defrun_batch(self,batch,pp_proxy_tensorsNone):self.forward_ct1ifself.is_generation:ifself.enable_overlap:withself.forward_stream_ctx:batch_resultself.model_worker.forward_batch_generation(batch,...)else:batch_resultself.model_worker.forward_batch_generation(batch,...)returnbatch_resultmodel_worker.forward_batch_generation内部构造ForwardBatch把 CPU 元数据搬到 GPU决定用 CUDA Graph 还是 eager 执行调用模型forward→ 得到 logits采样 → 得到下一个 token④ 处理结果 —process_batch_result把新生成的 token 追加到每个请求的output_ids检查是否finishedEOS / max_new_tokens / abort通过 ZMQ PUSH 把结果发给Detokenizer 进程不是直接发 TokenizerManager释放已完成请求的 KV Cache或保留在 RadixTree 中供后续复用Scheduler 全景时序图Scheduler 主循环每几毫秒一轮 │ ▼ ┌───────────────────────┐ │ ① recv_requests │ ← ZMQ PULL 拿新请求 │ → waiting_queue │ └───────────┬───────────┘ ▼ ┌───────────────────────┐ │ ② get_next_batch │ ← 调度大脑 │ │ │ a) 合并上批 prefill │ │ 到 running_batch │ │ │ │ b) 尝试凑 prefill │ ─→ RadixTree 找前缀 │ PrefillAdder │ ─→ KV Cache 分配器 │ │ ─→ Chunked Prefill 切块 │ │ │ c) 有 prefill? 跑 │ │ 没有? 跑 decode │ ─→ 淘汰 finished │ update_running │ ─→ retract 老请求必要时 └───────────┬───────────┘ ▼ ┌───────────────────────┐ │ ③ run_batch │ ← 提交 GPU forward │ ScheduleBatch │ │ ↓ │ │ ForwardBatch (GPU) │ │ ↓ │ │ model.forward │ ─→ CUDA Graph 或 eager │ ↓ │ │ sample → token IDs │ └───────────┬───────────┘ ▼ ┌───────────────────────┐ │ ④ process_batch_result│ │ • 追加 output_ids │ │ • 检查 finished │ │ • ZMQ PUSH → Detokenizer 进程 │ • 释放 KV Cache │ ─→ RadixTree 保留前缀 └───────────┬───────────┘ │ ▼ 回到 ①下一轮关键概念对照概念说明waiting_queue新请求排队等待被 prefillrunning_batch正在 decode 的请求集合last_batch上一轮跑的 batchoverlap 模式用来延迟处理ScheduleBatchCPU 侧的批次描述reqs 列表 元数据ForwardBatch从 ScheduleBatch 派生的 GPU 侧对象张量、input_ids 等Prefill 优先每轮先看能不能 prefill不能才 decode保 TTFTChunked Prefill超长 prompt 切块避免拖累 decode 延迟保 ITLRetract 机制显存紧张时把 running 请求踢回 waiting释放 KVOverlap Scheduler一步延迟让 CPU 处理结果和 GPU forward 并行3.5 各模块职责一览模块职责Uvicorn / FastAPIHTTP/TCP 协议层路由匹配中间件ChatCompletionRequestPydantic 模型协议级字段校验OpenAIServingChatOpenAI 协议 → 内部协议的翻译层GenerateReqInputSGLang 内部统一请求对象协议无关TokenizerManager请求生命周期管理tokenize与 Scheduler 通信Scheduler调度大脑批处理、KV Cache 管理、prefill/decode 决策ModelRunner执行模型前向计算CUDA Graph 管理Detokenizertoken IDs → 文本流式输出3.6 为什么要这么多层协议兼容OpenAI / Anthropic / Ollama 各协议靠不同Serving*类适配到同一个GenerateReqInput关注点分离HTTP 层不懂模型模型层不懂 HTTP多进程隔离HTTP 进程和 Scheduler 进程独立故障不互相影响CPU/GPU 解耦tokenize 是 CPU 密集不占用 GPU 时间片可观测性每层可独立打点、记录日志四、PD 分离Prefill-Decode Disaggregation代码位于python/sglang/srt/disaggregation/。核心思想将 Prefill计算密集和 Decode访存密集拆分到不同 GPU/节点上Prefill 节点 Decode 节点 ┌──────────────┐ KV Transfer ┌──────────────┐ │ 处理 prompt │ ─────────────────→│ 逐 token 生成│ │ 生成 KV Cache│ │ 使用 KV Cache│ └──────────────┘ └──────────────┘关键文件prefill.py— Prefill 节点逻辑decode.py— Decode 节点逻辑nixl/— 基于 NIXL 的高速 KV 传输RDMAmooncake/— Mooncake 存储后端mori/— 另一种传输后端kv_events.py— KV 传输事件管理decode_hicache_mixin.py— 结合 HiCache 的分层存储优势Prefill 节点可用计算型 GPUDecode 节点可用访存型 GPU各自独立扩缩容提高整体吞吐。五、KV Cache 优化代码位于python/sglang/srt/mem_cache/这是 SGLang 的一大亮点1. RadixTree 前缀缓存 (radix_cache.py)用基数树Radix Tree组织所有请求的 KV Cache相同前缀的请求共享 KV Cache避免重复计算典型场景多轮对话中 system prompt 只需计算一次2. 内存池 (memory_pool.py)预分配 GPU 显存以 pageblock为单位管理支持动态分配/回收避免显存碎片3. HiCache (hiradix_cache.py,hicache_storage.py)分层缓存GPU → CPU → Disk热数据留 GPU冷数据 offload 到 CPU/SSD实现大容量 KV Cache突破显存限制4. 分配策略 (allocation.py,evict_policy.py)LRU 等淘汰策略按优先级管理 cache 生命周期5. 统一缓存 (unified_cache/,unified_memory_pool.py)统一管理不同类型模型Attention Mamba/线性注意力的状态缓存六、量化Quantization代码位于python/sglang/srt/layers/quantization/支持非常丰富的量化方案量化方法文件说明FP8fp8.pyW8A8 FP8 量化主流方案INT8w8a8_int8.pyW8A8 INT8GPTQgptq/经典权重量化AWQawq/Activation-aware 量化MXFP4mxfp4.py微缩浮点 4bit新一代方案FP4fp4_utils.py,nvfp4_online.pyNVIDIA FP4BitsAndBytesbitsandbytes.py4/8bit 量化KV Cache 量化kv_cache.py,fp4_kv_cache_quant_method.py单独量化 KV Cache 节省显存MoE 专用moe_wna16.py,mxfp4_*_moe.pyMoE 模型专用量化KV Cache 量化特别值得关注即使模型权重不量化也可以单独将 KV Cache 量化到 FP8/FP4大幅节省显存允许更大的 batch size。七、其他重要特性Continuous BatchingScheduler 实现动态批处理新请求随时插入CUDA GraphDecode 阶段用 CUDA Graph 减少 kernel launch 开销投机解码(speculative/)用小模型预测多个 token大模型验证Tensor Parallelism / Expert Parallelismdistributed/下支持多种并行策略多种 Attention 后端FlashAttention、FlashInfer、MLADeepSeek、TRT-LLM 等八、总结SGLang 的核心竞争力在于RadixTree 前缀缓存 高效调度 PD 分离 丰富的量化支持使其在高并发推理场景下具有很强的吞吐优势。