home/tutorial/M1 入口

Month 1 · 入口与请求生命周期

目标:能在白板上画出 "curl 进来到 token 流出" 的完整调用链。

这个月不碰 KV cache、不碰 CUDA、不碰调度细节。 你只做一件事:跟着一个真实请求,把它走过的每一层代码都摸一遍。 这一层走通,后面所有深入细节都有"挂靠点"。

驱动问题
curl http://localhost:8000/v1/chat/completions ...。 这个 HTTP 请求会经过哪些 Python 对象、被哪些线程/进程处理、最后是谁真的把它送上 GPU? 画一张图,需要画几个框?
先猜一下层数(不用准确)

大概 5–7 层:HTTP server → API handler → engine wrapper (async) → engine core → scheduler → worker → model forward。 每层做的事不一样:HTTP 解析、tokenize、调度入队、batch 组装、GPU forward、detokenize、流式输出。

这一节就是去把这张图真正画对

01需要的先验知识

下面这些大致懂就行。模糊的现在补,不熟悉的标记一下,月底再看:

💡 触发的 CS162 章节
这个月会撞到的 OS 概念是 I/O 模型(阻塞/非阻塞/async/epoll)。 深入不必,知道"async 是一种把多个 I/O 操作交错在一个线程里"的实现就够。 真正用得上 OS 调度概念是 Month 3,那时候再展开。

02vLLM 的总体架构

记住两个版本概念:

新代码统统看 v1。下图是 v1 架构的简化版:

curl / SDK HTTP POST Process A · API Server FastAPI route /v1/chat/completions tokenize + 包成 EngineRequest AsyncLLM client ZMQ IPC → engine Process B · Engine Core EngineCore.run_busy_loop() Scheduler.schedule() 挑这一步的 batch BlockManager 分配 KV blocks Executor.execute_model() detokenize 输出 Process C..N · Workers (每张 GPU 一个) ModelRunner.execute_model() 组 input、forward model.forward() PyTorch / CUDA attention kernel csrc/ GPU 显存 · KV cache 物理块池 PagedAttention 在这里发挥
v1 把"接收请求"和"执行模型"拆到不同进程,靠 ZMQ 通信。这样 GIL 不再卡瓶颈。

03请求生命周期 · 一步一步

下面这套调用链是 v1 在 vLLM main 分支的形态(路径可能微调,但结构稳定)。 拿这张表去对照你 clone 下来的代码

#发生了什么关键代码位置
1HTTP 请求到 FastAPI route handlervllm/entrypoints/openai/api_server.py
2构造 ChatCompletionRequest,绑模板 → prompt textvllm/entrypoints/openai/serving_chat.py
3Tokenize prompt → list[int]vllm/transformers_utils/tokenizer.py
4构造 EngineCoreRequest 通过 ZMQ 发到 enginevllm/v1/engine/async_llm.py
5Engine 主循环把它加入 waiting 队列vllm/v1/engine/core.pyrun_busy_loop()
6每一步 schedule() 决定哪些 request 进入这一步的 batchvllm/v1/core/sched/scheduler.py
7BlockManager 给每个 sequence 分配 KV blocksvllm/v1/core/kv_cache_manager.py
8Executor 把 batch 序列化发给 workervllm/v1/executor/
9Worker 在 GPU 上跑 model.forward()vllm/v1/worker/gpu_model_runner.py
10Attention kernel 用 block table 访问 KVcsrc/attention/ (CUDA) 或 vllm/v1/attention/ (Triton)
11采样得到下一个 token,回写 KVvllm/v1/sample/sampler.py
12Detokenize 这个 token → str,发回 API servervllm/v1/engine/output_processor.py
13SSE 流式 yield 给 HTTP client回到 step 2 的 streaming generator
⚠ 路径可能小变
vLLM 改名/重构频繁。如果上面某个文件找不到,git grep:比如 git grep -l "run_busy_loop" 就能定位。 这是日常技能 —— 教程里的路径只是"线索",不是"地图"。

04这个月你要读的 5 段代码

下面 5 个 anchor,按顺序读完,每段带一个核心问题。

读 ① · 1–2 小时
HTTP 入口。看它怎么注册 routes、怎么启动 engine、怎么 graceful shutdown。
这里有几个 async 的"分叉点"?streaming response 是怎么从 engine 拿数据的?
读 ② · 1 小时
Chat 协议的具体处理:应用 chat template、处理 tool calling、构造 prompt。这是协议层,与 vLLM 引擎无关
如果我想加一个新的 sampling 参数,要改几个文件?
读 ③ · 2–3 小时(最重要)
两个进程通过 ZMQ 通信的本体。这里读懂,你就理解了"为什么 v1 比 v0 快"
API server 进程和 engine 进程各持有什么状态?它们之间发什么消息?为什么不放一个进程?
读 ④ · 30 分钟(先看结构,不细究算法)
不要试图全看懂。只看:函数签名、return 什么、循环结构。 Month 3 会深入。
这个函数的 input 是什么 state?output 是什么 state?
读 ⑤ · 30 分钟
Worker 这一头。看它怎么把 scheduler 给的 batch 转成 PyTorch 输入。
PagedAttention 在哪里"接通"了 KV cache?看 attn_metadata 怎么构造的。

05动手作业 · 画一张请求生命周期图

读完上面 5 段,不看任何东西,自己在白板/Notion/纸上画:

  1. 所有出现的进程 / 线程框(标注哪个是 async 的、哪个是 worker subprocess)
  2. 每个框里存的关键状态(队列、cache、KV pool)
  3. 跨进程的消息箭头(ZMQ payload 是什么形状)
  4. "一次 decode step" 这条主路径标红

画完后跟本文 §02 那张图对照。发现的不一致就是你下个月的研究方向

06第一个非 trivial 的 PR(可选)

Month 1 KPI 是至少一个 PR 已 merge。除了 Week 0 的 typo PR,本月可选挑战:

💡 找 issue 的诀窍
label:good-first-issue 标签下的 issue 通常已被人盯,抢手。 更可靠的来源:看最近 merged PR 的 reviewer 留言里 "we should also do X..." 这种隐藏 todo,主动 ping 那位 reviewer。

07本页自检

Month 1 结束时这些应该全部 ✓