| 系统 | Preferred Prefill Worker 选择 | Decode Worker 选择 | KV 路由方向 | Lease/失效机制 |
|---|---|---|---|---|
| vLLM NixlConnector | Router 任选可用 P;Proxy 轮询 P | P 完成 prefill 后由 Router/Proxy 以 kv_transfer_params 选定 D | pull (默认 D READ) / push (P WRITE) | lease + heartbeat,D 在 queueing/in-flight 期续租 |
| NVIDIA Dynamo | Planner 按 SLA 输出 P 池大小;Router KV-aware scoring | Router 用 workload + cache-overlap 打分 | pull (READ via NIXL/NVLink) | ModelExpress weight streaming;Canary liveness |
| SGLang PD | 客户端直送 P 端;sgl_router --pd-disaggregation 轮询/负载均衡 | Bootstrap server 协调:P 向 D 主动推送 transfer_info 选择某 D | push:P-side sender 主动 WRITE/transfer KV indices to D's prealloc 地址;receiver 等待 poll | bootstrap_timeout (默认 300s);HiCache restore 失败回退本地重 prefill |
| LMCache MP + NixlConnector | 与 vLLM 相同(共享 NixlConnector);LMCacheMPConnector 叠加跨请求 KV 复用 | Router --vllm-pd-disaggregation 模式 | pull:D READ from P over NIXL;MP server (CPU DRAM) 作为第二源(本地 GPU → LMCache → recompute) | L1 TTL;P2P 跨节点 RDMA lock-and-locate;NIXL lease(复用 vLLM lease) |
① Request arrives → Router 选 P:Router/Proxy 挑一个可用 prefill instance(策略由上层决定,vLLM 自身仅暴露 do_remote_decode/do_remote_prefill 两个标志位)。
② P 执行 prefill:跑 full prefill,得到 KV cache blocks,提交 NIXL READ-ready 状态。
③ P 返回 kv_transfer_params:通过 HTTP response 将 P 的 NIXL 侧信道 (host/port/tp_size/engine_id/block 元信息) 给 Proxy。
④ Proxy 选 D 并签发 decode leg:D 端 worker pre-allocates 等量 KV blocks,对 P 发起 RDMA READ。
⑤ D 读取完成后通知 P:P 释放 blocks(lease 到期或 transfer 完成即释放)。
docs/design/nixl_kv_cache_lease.md动机:默认 pull 模式下 D 必须主动拉,两侧握手顺序和 watchdog 逻辑复杂;push 让 P 直接 WRITE 到 D 预注册内存,"发送即完成"。
新增 nixl-push-writer 线程:每个 TP rank 起一个 background writer 线程,仅负责 nixl_wrapper.get_new_notifs / make_prepped_xfer / transfer(WRITE)。
Writer-local matching tables 实现两侧顺序无关的 request_id 匹配:_pending_d_registrations(D 侧发来的 PUSH_REG)与 _push_finished_blocks(P 侧 scheduler stage 的 blocks)。时间对齐用 get_base_request_id() 剥离 cmpl-uuid-index 后的 8-hex 随机 suffix。关键:这是 Proxy 给两腿的同一 X-Request-Id 被 input_processor 随机化后的解同步手段
Wire format:D→P 的 PUSH_REG 通过 NIXL notification 发 PUSH_REG:<msgpack>,字段:request_id, decode_engine_id, decode_host, decode_port, decode_tp_size, local_block_ids (per-group), remote_engine_id。
Transfer 流程:
D allocate → stash → worker build_connector_meta → meta.push_registrations → writer enqueue PUSH_REG → NIXL send_notif → P writer 收到 → _handle_push_reg_notif P prefill finish → meta.push_finished_blocks → writer 匹配 → NIXL WRITE → D → D 收到 completion notif → update_connector_output(finished_recving) → stop watchdog
Wake model:三个事件源 + 1ms self-poll:① start_load_kv 握手产生新材料;② get_finished 始终唤醒(让 writer drain HB / completion / late PUSH_REG);③ handshake 完成回调。
设计动机:单一大超时 480s 遇 D crash 保活浪费显存;过短则易误杀排队的 D。解法:lease renewal via heartbeat。
Lease 默认参数:initial lease 30s;heartbeat interval ≈5s(lease_duration//6);每次续租延长 ≈20s(2/3 lease);P 校验用 max(old_expiry, now + lease_extension)(never 缩短)。
关键设计:D 端 on_new_request() 从 request 进入 scheduler waiting queue 就开始发 HB,不等被 schedule 执行。所以 prefill 完成时 P blocks 立即进入 "leased" 状态。
HB 基于 request:不是 per-instance,而是 per-request。这样 P 无需知道 "哪个 D 持有我的 blocks",路由解耦。
Transport:复用 NIXL send_notif/get_new_notifs,后端 IB/RoCE/TCP 自动 fallback。HB 消息格式 HB:<payload>,每个 HB 消息可批量续租同一 P 的所有 in-flight requests。
Proactive handshake:D 发 HB 时若尚未与 P 握手(请求还在排队),就触发 background thread 主动完成握手,加速后续 KV READ。
vLLM 自身不实现 Router,而是提供 Connector / LookupBuffer / Pipe 三层抽象:
insert(非阻塞)+ drop_select(阻塞)KV cache 缓冲区。send_tensor / recv_tensor。每个进程一个 connector:Scheduler connector(调度 KV transfer ops)+ Worker connectors(执行 KV transfer ops)。
Connector 实现:NixlConnector、LMCacheConnectorV1、MooncakeConnector、MoRIIOConnector、MultiConnector、OffloadingConnector、FlexKVConnectorV1。
docs/features/disagg_prefill.md两种入口:
client → Frontend → Router → workers,无需外部 gateway。client → Gateway → EPP (Endpoint Picker Plugin) → Frontend sidecar (router-mode direct) → workers。Router 决策基于 worker load + KV cache overlap(KV-aware routing),消除冗余 prefill 计算。
| 能力 | 作用 | 与 PD 路由的关系 |
|---|---|---|
| Disaggregated Prefill/Decode | P/D 独立可扩展 GPU 池 | Router 把 prefill leg 和 decode leg 分别路由到 P 池和 D 池 |
| KV-Aware Routing | 基于 worker load + KV cache overlap 路由 | 避免重复 prefill,2x TTFT 加速 |
| KVBM (KV Block Manager) | GPU → CPU → SSD → remote 多级 KV 卸载 | Router 可感知 KV 在远端存储中的位置 |
| ModelExpress | NIXL/NVLink GPU-to-GPU 权重流式加载 | 7x 冷启动加速,新 replica 快速上线 |
| Planner | SLA-driven autoscaler,profile + right-size pools | 决定 P/D 池大小,间接影响路由选择空间 |
| Grove (K8s operator) | 拓扑感知 gang scheduling (NVL72) | 把 workload 放到最优 rack/host/NUMA |
| Fault Tolerance | Canary health check + in-flight request migration | Worker 失败时请求不丢 |
sgl_router 进程,--pd-disaggregation 模式把请求先送 P 再送 D。启动命令:
# P 端 python -m sglang.launch_server --model-path ... --disaggregation-mode prefill --port 30000 # D 端 python -m sglang.launch_server --model-path ... --disaggregation-mode decode --port 30001 # Router python -m sglang_router.launch_router --pd-disaggregation \ --prefill http://127.0.0.1:30000 --decode http://127.0.0.1:30001 --host 0.0.0.0 --port 8000
Router 把请求先送 P,P 完成 prefill 后把 transfer_info 给 Router,Router 再送 D。
四个阶段:
1. Bootstrap Queue a. 为每个 request 初始化 sender b. 用 queue 存储 bootstrap 未完成的 requests c. poll senders 检查 bootstrap 状态 d. bootstrap 完成后移到 Waiting Queue 2. Waiting Queue a. PrefillAdder pop requests b. 跑 forward c. 移到 Inflight Queue 3. Inflight Queue a. 非阻塞 poll sender b. transfer 完成后 return request
Sender 通过 bootstrap_room 与 D 端 receiver 握手,bootstrap_addr 是 P 端的 HTTP bootstrap server 地址。
四个阶段:
1. PreallocQueue a. 为每个 request 初始化 receiver b. request 先 handshake,有可用 KV 时 pre-allocate c. 移到 TransferQueue 2. TransferQueue a. poll receiver 检查 transfer 状态 b. transfer 完成后移到 waiting queue 3. WaitingQueue a. 用 queue 中的 requests 构造 PrebuiltExtendBatch b. 跳过 prefill forward,只填充 metadata 4. RunningBatch a. 把 PrebuiltExtendBatch 合并进 running batch 跑 decoding
关键:D 端 跳过 prefill forward,直接用 P 端传来的 KV 构造 PrebuiltExtendBatch,省掉 prefill 计算。
CommonKVBootstrapServer:只在 P 端启动,HTTP 服务,D 端 receiver 通过它发现 P 端 sender。
CommonKVManager:每个 TP rank 一个,bind ZMQ PULL socket,管理 request_status / socket_cache / failure_records。
CommonKVSender.init(num_kv_indices, aux_index):设置本地 request 的 index metadata。
CommonKVSender.send(kv_indices, state_indices):把指定 indices 的 KV cache 推到 D 端。
CommonKVSender.poll():检查 transfer 状态,返回 KVPoll (Failed/Bootstrapping/WaitingForInput/Transferring/Success)。
DP attention 下的 prefill dp_rank 注册:_register_prefill_dp_rank() 把 P 端的 dp_rank 注册到 bootstrap server,D 端据此选择正确的 P rank。follow_bootstrap_room 模式用 bootstrap_room % dp_size 决定。
2717 行,核心:
nixlRemoteDisconnectError / nixlBackendError / nixlCancelledError 统一捕获。TransferInfo 结构:
@dataclasses.dataclass
class TransferInfo:
room: int
endpoint: str
dst_port: int
mooncake_session_id: str
dst_kv_indices: npt.NDArray[np.int32]
dst_aux_index: int
dst_state_indices: List[List[int]]
required_dst_info_num: int
is_dummy: bool
decode_prefix_len: Optional[int] = None
staging: Optional[StagingTransferInfo] = None
Mooncake 用 mooncake_session_id 标识 session,支持 SGLANG_MOONCAKE_CUSTOM_MEM_POOL 环境变量选择 NVLINK / INTRA_NODE_NVLINK / BAREX 模式。
DecodeHiCachePreallocMixin / DecodeHiCacheTransferMixin:D 端 mixin,支持从 P 端恢复 KV cache 时做 prefix match。
HiCacheRestoreGatedKVReceiver:带门控的 KV receiver,restore 失败时回退到本地重 prefill。
DecodePrefixMatch:在 D 端 radix cache 中匹配已有 prefix,减少需要传输的 KV 量。
sgl_router --pd-disaggregation --prefill <urls> --decode <urls>
Router 把请求先送 P,P 完成 prefill 后把 transfer_info 给 Router,Router 再送 D。
环境变量:
SGLANG_DISAGGREGATION_THREAD_POOL_SIZE:每 TP rank 的 KV transfer 线程数(默认 0.75*cpu_count/8,限制 4-12)。SGLANG_DISAGGREGATION_QUEUE_SIZE:并行 transfer 队列数(默认 4),多 D 请求共享带宽。SGLANG_DISAGGREGATION_BOOTSTRAP_TIMEOUT:bootstrap 超时(默认 300s)。SGLANG_DISAGGREGATION_HEARTBEAT_INTERVAL / MAX_FAILURE:D→P 心跳(默认 5s / 2 次失败标记 offline)。
vLLM Instance(s)
|
| ZMQ (tcp)
v
MessageQueueServer (mq.py)
|
| dispatch by RequestType
v
MPCacheServer (server.py)
|
|--- TokenHasher / SessionManager
|
v
StorageManager (distributed/storage_manager.py)
|
|--- L1Manager (l1_manager.py)
| |--- L1MemoryManager (CPU DRAM) or
| | GDSL1MemoryManager (NVMe slab via cuFile / hipFile)
| |--- TTLLock per object (read/write)
|
|--- StoreController -----> L2 Adapter(s) (async L1->L2 push)
|--- PrefetchController ---> L2 Adapter(s) (async L2->L1 load)
|--- EvictionController ----> L1Manager (watermark-triggered eviction)
|
v
EventBus + OTel providers (observability)
Server 入口:lmcache server(推荐,ZMQ + FastAPI HTTP frontend)或 python3 -m lmcache.v1.multiprocess.server(legacy)。
最小部署 5 个进程:
MultiConnector[NixlConnector (kv_producer), LMCacheMPConnector]MultiConnector[NixlConnector (kv_consumer), LMCacheMPConnector]vllm-router --vllm-pd-disaggregationKV source order on prefill path:本地 GPU cache → LMCache → recompute。
配置:
--kv-transfer-config '{"kv_connector":"MultiConnector","kv_role":"kv_both",
"kv_connector_extra_config":{"connectors":[
{"kv_connector":"NixlConnector","kv_role":"kv_both"},
{"kv_connector":"LMCacheMPConnector","kv_role":"kv_both"}]}}'
关键 PR:vllm-project/vllm#46865(MultiConnector real-blocks fix)是 LMCache offload 的前置条件。
问题:多节点部署时,每个节点独立缓存,相同 prefix 在不同节点需重复 prefill。
解法:P2P 把 per-node caches 变成逻辑上的一个 cache。
三组件:
流程:cache miss → 向拥有 prefix 的 peer 请求 lock-and-locate → 拿到远端地址 → RDMA read 到本地 L1 → 服务请求。
特点:one-sided RDMA read,拥有数据的 node 不被中断;peer 自动发现/断开。
vLLM ↔ LMCache 用 ZMQ DEALER/ROUTER 模式通信。
RequestType 枚举:
| Request Type | Handler | 描述 |
|---|---|---|
| REGISTER_KV_CACHE | SYNC | 注册 GPU KV cache tensors |
| UNREGISTER_KV_CACHE | SYNC | 反注册 |
| STORE | BLOCKING | GPU → L1 (CPU),LMCache-driven,CUDA IPC |
| RETRIEVE | BLOCKING | L1 (CPU) → GPU,LMCache-driven,CUDA IPC |
| PREPARE_STORE / COMMIT_STORE | BLOCKING | Engine-driven path,CPU/accelerator workers |
| PREPARE_RETRIEVE / COMMIT_RETRIEVE | BLOCKING | Engine-driven path |
Transfer mode:--supported-transfer-mode 可选 lmcache_driven / engine_driven / auto(默认)。
github.com/MoonshotAI/Mooncake 仓库已 404(可能已删除或改名)。本报告基于 SGLang/vLLM 集成文档和 stmatengss/SuSG2025-MooncakeTransferEngine 等间接信息。--disaggregation-transfer-backend mooncake,环境变量 MOONCAKE_TE_META_DATA_SERVER / MOONCAKE_MASTER / MOONCAKE_PROTOCOL / MOONCAKE_GLOBAL_SEGMENT_SIZE。MooncakeConnector / MooncakeStoreConnector,见 docs/features/disagg_prefill.md。SGLANG_MOONCAKE_CUSTOM_MEM_POOL=NVLINK + MC_FORCE_MNNVL=True(NVL72 推荐)。SGLANG_MOONCAKE_CUSTOM_MEM_POOL=INTRA_NODE_NVLINK + MC_INTRANODE_NVLINK=true(A100/H20/H100)。| 维度 | vLLM | NVIDIA Dynamo | SGLang | LMCache MP |
|---|---|---|---|---|
| KV 路由方向 | pull (默认) / push (NixlPushConnector) | pull | push (sender → receiver) | pull (NixlConnector) + MP server |
| Preferred P 选择 | Router/Proxy 任选 | Planner + Router KV-aware | sgl_router 轮询/负载均衡 | Router 任选 |
| Decode Worker 选择 | Proxy 拿到 kv_transfer_params 后选 D | Router 用 workload + cache-overlap 打分 | Bootstrap server 协调,P 主动推 | Router --vllm-pd-disaggregation |
| Lease 机制 | lease + heartbeat,D 从 queueing 开始续租 | Canary health check | bootstrap_timeout (300s) | L1 TTL;NIXL lease |
| KV Pool 感知 | LookupBuffer (SQL 语义) | KVBM (GPU→CPU→SSD→remote) | radix cache prefix match | L1 (CPU DRAM) + L2 (NIXL/GDS) |
| 跨请求 KV 复用 | Automatic Prefix Caching | KV-aware routing 避免重复 prefill | HiCache restore | LMCacheMPConnector + P2P |
| 传输后端 | NIXL (UCX/GDS) | NIXL/NVLink | Mooncake / NIXL / Ascend | NIXL + ZMQ |
| Router 位置 | 外部 (Proxy) | 内置 (Dynamo Frontend + Router) | 内置 (sgl_router) | 外部 (vllm-router) |
Pull 模式下 D 主动拉,D 端 scheduler 决定何时传输,与本地 batch 调度天然对齐;但 D 端需要知道 P 的地址和 block 布局,耦合度高。Push 模式下 P 主动推,P 端完成即可发送,D 端只需等待 poll;但 D 端需要预分配内存,且需要处理 "P 推完了但 D 还没 prealloc" 的竞态。vLLM 的 NixlPushConnector 用 writer-local matching tables 解决这个竞态。
vLLM 的 lease + heartbeat 设计精妙之处在于:lease 是 per-request 不是 per-instance。P 无需知道哪个 D 持有它的 blocks,D 也无需知道 P 的 block 布局。这实现了 P/D 的完全解耦,Router 可以自由选择 D 而不影响 P 的 block 管理。
SGLang 的 bootstrap server 是 P 端的一个 HTTP 服务,D 端 receiver 通过它发现 P 端 sender。这个设计把 "P 在哪里" 的发现逻辑从 Router 解耦出来,Router 只需要知道 P 的 bootstrap server 地址即可。bootstrap_room 是 request 级别的会话标识。
SGLang 的 HiCache 在 D 端 radix cache 中匹配已有 prefix,只传输 "缺失的 suffix"。这大幅减少 KV 传输量,尤其在多轮对话场景。DecodePrefixMatch + HiCacheRestoreGatedKVReceiver 的组合是工程上的精巧设计。
LMCache 把 KV cache 从 vLLM 进程中完全剥离,变成独立 daemon。这带来:① 进程隔离(cache 问题不 crash 推理引擎);② 跨 pod 共享 L1 缓存;③ 多级存储 (L1 CPU DRAM + L2 NVMe/remote);④ P2P 跨节点 RDMA 共享。这是 "KV Cache as a Service" 的范式。
Mooncake 仓库已 404,NVIDIA Dynamo 的 Router 内部实现(scoring 函数、KVBM 细节)未完全开源。实际生产系统的路由策略可能远超公开水平。
| # | URL | 状态 | 备注 |
|---|---|---|---|
| 1 | raw.githubusercontent.com/vllm-project/vllm/main/docs/design/nixl_kv_push_connector.md | ✅ OK | 8500+ chars 本地保存 |
| 2 | raw.githubusercontent.com/vllm-project/vllm/main/docs/design/nixl_kv_cache_lease.md | ✅ OK | 完整设计文档 |
| 3 | docs.vllm.ai/en/latest/features/disagg_prefill.html | ❌ 404 | Rerouted |
| 3b | raw.githubusercontent.com/vllm-project/vllm/main/docs/features/disagg_prefill.md | ✅ OK (curl) | Markdown 成功获取 |
| 4 | github.com/ai-dynamo/dynamo | ✅ OK | README 7.5K stars |
| 5 | docs.sglang.ai/references/disagg.html | ❌ 404 | Not found |
| 5b | raw.githubusercontent.com/sgl-project/sglang/main/docs_new/docs/advanced_features/pd_disaggregation.mdx | ✅ OK | 用户文档 |
| 5c | github.com/sgl-project/sglang/blob/main/python/sglang/srt/disaggregation/{prefill,decode,nixl/conn,mooncake/conn,decode_hicache_mixin,common/conn}.py | ✅ OK | 源码级 PD routing 内部实现 |
| 6 | github.com/MoonshotAI/Mooncake | ❌ 404 | 仓库已删除/改名 |
| 7 | docs.lmcache.ai/ | ✅ OK | 主站 |
| 7b | raw.githubusercontent.com/LMCache/LMCache/dev/docs/source/mp/{index,disaggregated_prefill,p2p}.rst | ✅ OK | MP 模式内部实现 |
| 7c | raw.githubusercontent.com/LMCache/LMCache/dev/docs/source/disaggregated_prefill/nixl/1p1d.rst | ✅ OK | 1P1D 示例 |
所有原始资料保存在 /Users/backyes/work/claude_workspace/pd_routing_research/:
vllm_nixl_kv_push_connector.txt — vLLM push 模式设计文档vllm_nixl_kv_cache_lease.txt — vLLM lease 机制设计文档vllm_disagg_prefill.md — vLLM PD 分离抽象层dynamo_readme.txt — NVIDIA Dynamo READMEsglang_pd_disaggregation.mdx — SGLang PD 用户文档lmcache_mp_index.rst — LMCache MP 模式架构lmcache_mp_disagg.rst — LMCache MP PD 分离lmcache_p2p.rst — LMCache P2P KV 共享lmcache_nixl_1p1d.rst — LMCache 1P1D 示例url_status.md — URL 访问状态