LLM inference system overview
¶1. 智能流量路由(Smart Router)
作为系统的流量入口,Router 不仅充当网关的角色,更是保障服务稳定性与会话连续性的第一道防线。
-
会话保持(Session Stickiness):
基于请求内容(Request Content)生成的唯一指纹或 Session ID,Router 会精确记录每个会话与后端节点的映射关系。确保同一会话的后续请求被路由至同一固定节点,从而最大化利用本地缓存(KV Cache),避免重复计算带来的资源浪费。 -
动态负载均衡与热点防御:
为防止单节点因流量突增而过载(Hotspot),系统引入了严格的 Server Load Threshold(服务器负载阈值) 机制。当特定节点的负载触及红线时,Router 将智能介入,进行流量整形或请求重路由,确保集群整体的健康度与吞吐量。
¶2. KV Cache Offload
显存容量是制约 LLM 推理吞吐量的主要瓶颈。为了突破单卡显存限制,L1-L4 多级流水线式 的 KV Cache 存储架构,按访问速度分层管理:
- L1 (GPU HBM): 存放最活跃的 Hot KV Cache,确保核心计算微秒级响应。
- L2 (CPU Memory): 利用 Host 内存作为次级缓冲区,通过 PCIe 快速交换数据。
- L3 (Remote GPU via RDMA): 当本地资源耗尽时,通过 RDMA(远程直接内存访问)技术极速“借用”其他空闲节点的显存,实现显存池化。
- L4 (NVMe SSD): 作为冷数据的最终落脚点,提供海量存储兜底。
技术推荐 Pegaflow:
针对单节点多 GPU 场景,Pegaflow 提供了一套高性能的卸载方案,能有效提升推理引擎的显存利用效率。延伸阅读: 可参考 Dynamo KVBM 和 llm-d-kv-cache 等相关技术实现。
¶3. P/D 分离架构(Prefill/Decode Disaggregation)
在高度互动的场景中(如 AI 伴侣、实时对话 App),用户对首字延迟(TTFT)和生成流畅度(TPOT)极为敏感。传统的混合部署模式下,高负载的 Prefill(预填充)阶段往往会抢占计算资源,导致 Decode(解码)阶段卡顿。
我们采用了 Prefill 与 Decode 分离 的解耦架构:
-
核心优势: 彻底隔离计算密集型的 Prefill 阶段与访存密集型的 Decode 阶段。无论新请求如何涌入,Decode 节点始终拥有独立的计算资源,保证输出如丝般顺滑,不被中断。
-
关键技术依赖:
- 可靠的 RDMA 网络: 该架构要求 Prefill 节点计算完的 KV 状态能以极低延迟传输至 Decode 节点。
- 增量传输机制: 仅需传输新增 Token 对应的 KV 数据,而非全量搬运,大幅降低网络带宽压力。
- Decode 本地缓存: 接收端节点维护热数据缓存,进一步加速生成。
-
传输后端(Transfer Backend)选型:
- NIXL
- Mooncake (KVCache 分离架构的典型代表)
¶推理引擎
flowchart TB
%% 样式定义
classDef routerStyle fill:#4A90D9,stroke:#2C5F8A,color:#fff,stroke-width:2px,font-size:14px
classDef prefillHitStyle fill:#27AE60,stroke:#1E8449,color:#fff,stroke-width:2px
classDef prefillMissStyle fill:#E67E22,stroke:#BA6617,color:#fff,stroke-width:2px
classDef decodeStyle fill:#9B59B6,stroke:#7D3C98,color:#fff,stroke-width:2px
classDef pegaStyle fill:#1ABC9C,stroke:#16A085,color:#fff,stroke-width:1px
classDef clientStyle fill:#34495E,stroke:#2C3E50,color:#fff,stroke-width:2px
%% Client
CLIENT([🖥️ Client Request]):::clientStyle
%% Router
ROUTER["🌐 Router<br/> session亲和<br/>一致性哈希<br/>load aware"]:::routerStyle
CLIENT --> ROUTER
%% ===== Prefill Hit Cache =====
subgraph PH["Prefill 命中缓存"]
direction TB
PH_NODE["⚡ Prefill Node<br/>(Cache Hit)<br/>─────────────<br/>• 本地命中前缀<br/>• 仅计算增量部分"]:::prefillHitStyle
PH_PEGA[("🗄️ PegaFlow<br/>本机 CPU/SSD<br/>─────────────<br/>• 已有 KV Cache<br/>• 命中 → 直接复用")]:::pegaStyle
PH_PEGA <-->|"KV Load/Save"| PH_NODE
end
%% ===== Prefill Miss Cache =====
subgraph PM["Prefill 未命中缓存"]
direction TB
PM_NODE["⚡ Prefill Node<br/>(Cache Miss)<br/>─────────────<br/>• 首次请求无缓存<br/>执行完整 Prefill<br/>•后续请求无缓存<br/>从其他节点pull"]:::prefillMissStyle
PM_PEGA[("🗄️ PegaFlow<br/>本机 CPU/SSD<br/>─────────────<br/>• 计算后写入 KV<br/>• 供后续请求复用")]:::pegaStyle
PM_NODE -->|"Prefill 完成<br/>KV Save"| PM_PEGA
end
%% ===== Decode =====
subgraph DEC["Decode"]
direction TB
D_NODE["🔄 Decode Node<br/>─────────────<br/>• 接收 KV Cache<br/>"]:::decodeStyle
D_PEGA[("🗄️ PegaFlow<br/>本机 CPU/SSD<br/>─────────────<br/>• 缓存历史 KV<br/>• 多轮对话复用")]:::pegaStyle
D_PEGA <-->|"KV Load/Save"| D_NODE
end
%% ===== Router → 组件连线 =====
ROUTER -->|"① Cache Hit<br/>路由到有缓存的 Prefill"| PH_NODE
ROUTER -->|"② Cache Miss<br/>路由到任意 Prefill"| PM_NODE
ROUTER -->|"③ 后续请求<br/>Load-Aware 直发 Decode"| D_NODE
%% ===== Prefill → Decode 连线 (NIXL) =====
PH_NODE ==>|"📡 NIXL 传输<br/>Delta KV (仅增量)"| D_NODE
PM_NODE ==>|"📡 NIXL 传输<br/>Delta KV (仅增量)"| D_NODE
%% ===== Prefill 间 PegaFlow 跨节点 =====
PM_PEGA -.->|"🔎 PegaFlow Master 查询<br/>跨节点 Pull KV"| PH_PEGA
sequenceDiagram
autonumber
participant C as 🖥️ Client
participant R as 🌐 Router
participant PM as ⚡ Prefill Node (Miss)
participant PM_P as 🗄️ PegaFlow (Prefill Miss)
participant PH as ⚡ Prefill Node (Hit)
participant PH_P as 🗄️ PegaFlow (Prefill Hit)
participant D as 🔄 Decode Node
participant D_P as 🗄️ PegaFlow (Decode)
rect rgb(159, 159, 191)
Note over C,D_P: 📍 第 1 轮 — 首次提问(Cache Miss,完整 Prefill)
C->>R: 发送请求 (System Prompt + Q1)
R->>R: 查询 session 映射 ❌ 无缓存
R->>PM: Cache Miss 路由到任意 Prefill
PM->>PM: 执行完整 Prefill (全量计算)
PM->>PM_P: KV Save 全量写入 CPU/SSD
PM->>D: 📡 NIXL 全量 KV Cache (RDMA)
D->>D_P: KV Save 缓存第1轮KV
loop 逐 Token 生成
D->>D: Decode 生成 Token
D-->>C: Stream Token
end
R->>R: 记录 session 到 PM 节点映射
end
rect rgb(109, 182, 133)
Note over C,D_P: 📍 第 2 轮 — 追问(Cache Hit,增量 Prefill)
C->>R: 发送请求 (历史上下文 + Q2)
R->>R: 查询 session 映射 ✅ 命中 PM 节点
R->>PH: Cache Hit 路由到有缓存的 Prefill (PM变为PH角色)
PH_P->>PH: KV Load 加载第1轮KV到GPU
PH->>PH: 仅计算增量 Prefill (只处理Q2)
PH->>PH_P: KV Save 更新完整KV
par NIXL增量传输 与 Decode本地加载 并行
PH->>D: 📡 NIXL Delta KV (仅Q2增量)
D_P->>D: KV Load 加载第1轮历史KV
end
D->>D: 合并历史KV + Delta KV
D->>D_P: KV Save 更新缓存
loop 逐 Token 生成
D->>D: Decode 生成 Token
D-->>C: Stream Token
end
end
rect rgb(180, 100, 100)
Note over C,D_P: 📍 第 3 轮 — 异常场景:原节点过载,触发重路由
C->>R: 发送请求 (历史上下文 + Q3)
R->>R: ✅ 命中PH节点 但 ⚠️ 负载超阈值
R->>PM: 重路由到新 Prefill 节点 (Cache Miss)
PM->>PM_P: 本地查询KV ❌ 未命中
rect rgb(180, 100, 100)
Note over PM_P,PH_P: 🔎 PegaFlow 跨节点 Pull
PM_P->>PM_P: 向 PegaFlow Master 查询谁有此前缀KV
PM_P-->>PH_P: Master返回 PH节点有缓存
PH_P->>PM_P: 跨节点 Pull KV (RDMA) 传输第1到2轮完整KV
end
PM_P->>PM: KV Load Pulled KV到GPU
PM->>PM: 仅计算增量 Prefill (只处理Q3)
PM->>PM_P: KV Save 完整KV
par NIXL增量传输 与 Decode本地加载 并行
PM->>D: 📡 NIXL Delta KV (仅Q3增量)
D_P->>D: KV Load 加载第1到2轮历史KV
end
D->>D: 合并历史KV + Delta KV
D->>D_P: KV Save 更新缓存
loop 逐 Token 生成
D->>D: Decode 生成 Token
D-->>C: Stream Token
end
R->>R: 更新 session 到 PM 节点映射
end



