llm-d 架构解析(上):从 InferencePool 到 KV Cache-Aware Routing
引言
vLLM、SGLang 等推理引擎解决的核心问题,是如何在单个 Model Server 内高效完成模型加载、请求批处理、Prefill、Decode 与 KV Cache 管理。当系统从单实例扩展为由多个 Model Server 组成的推理集群后,新的问题随之出现:
- 一个请求应当被发送到哪个 Model Server?
- 如何避免部分实例过载而另一些实例空闲?
- 如果某个实例已经缓存了请求前缀对应的 KV Cache,能否优先复用?
- 当实例数量动态变化时,如何维护可用 Endpoint 的视图?
llm-d 关注的正是这一层问题。它并不替代 vLLM 或 SGLang,而是在多个推理实例之上提供面向 LLM 工作负载的请求路由、Endpoint 调度与 KV Cache 感知能力。
本文聚焦 llm-d 的核心架构与智能路由机制。P/D Disaggregation、KV Offloading、Autoscaling 与 Batch Serving 等能力将在下篇展开。
1. llm-d 的定位
可以将 llm-d 与推理引擎的职责划分为两个层次:
| 层次 | 主要职责 | 典型组件 |
|---|---|---|
| 单实例推理 | 模型加载、Continuous Batching、Prefill、Decode、KV Cache | vLLM、SGLang、TensorRT-LLM |
| 分布式 Serving | Endpoint 发现、请求调度、负载感知、Prefix Cache 感知 | llm-d |
Model Server 是实际执行推理的计算层。以 vLLM 为例,一个 Model Server 会加载模型、管理 GPU 上的 KV Cache,并暴露 OpenAI-compatible API 以及 Queue Depth、Running Requests、KV Cache Utilization 等指标。
llm-d 并不直接执行模型计算,而是根据这些运行状态决定请求应当由哪个 Model Server 处理。
2. 核心组件:InferencePool、Proxy 与 EPP
llm-d 的整体关系需要区分两条路径:请求数据路径与调度控制路径。前者负责真正转发推理请求,后者负责维护候选 Endpoint 并完成选路。
%%{init: {"look": "handDrawn", "theme": "base", "handDrawnSeed": 7}}%%
flowchart TB
subgraph CTRL["Scheduling / Control"]
direction LR
IP["InferencePool<br/>selector / targetPorts"]
E["EPP<br/>Endpoint Picker"]
IP -. "candidate endpoint definition" .-> E
end
subgraph DATA["Request Data Path"]
direction LR
C[Client] --> G["Gateway / Proxy"]
G -->|"forward request"| M["Selected Model Server<br/>vLLM / SGLang"]
M --> GPU[GPU]
end
G -. "scheduling request" .-> E
E -. "selected endpoint" .-> G
E -. "discover / observe endpoints" .-> M
classDef entry fill:#EEE8FF,stroke:#7663C6,stroke-width:1.5px,color:#222
classDef control fill:#D8F5F2,stroke:#3A9C96,stroke-width:1.5px,color:#222
classDef compute fill:#FFF2BF,stroke:#B9952A,stroke-width:1.5px,color:#222
class C,G,IP entry
class E control
class M,GPU compute
图中的实线表示请求数据路径:请求由 Client 进入 Gateway/Proxy,随后被转发至选定的 Model Server。虚线表示调度与状态关系:InferencePool 描述候选 Endpoint 集合,EPP 基于这些候选 Endpoint 及其运行状态做出选路决策,再将结果返回给 Proxy。
因此,InferencePool 并不是请求经过的数据面组件,EPP 也不承载模型推理流量。真正的数据转发仍由 Proxy 与 Model Server 完成。
2.1 Model Server
Model Server 是真正执行推理的组件,例如 vLLM 或 SGLang。它负责:
- 加载模型并占用 GPU 等加速器资源;
- 执行 Prefill 与 Decode;
- 管理 KV Cache;
- 暴露推理 API;
- 暴露 Queue、Running Requests、KV Cache Utilization 等运行指标。
因此,llm-d 的调度对象并不是 GPU 本身,而是提供推理能力的 Endpoint。在多数普通部署中,一个 Endpoint 可以近似理解为一个 Model Server Pod 的 IP:Port。
2.2 InferencePool
InferencePool 描述一组可以被 EPP 调度的 Model Server Endpoint。
其核心职责有两项:
- Endpoint Discovery:通过 Kubernetes Label Selector 发现符合条件的 Model Server Pod,并跟踪其 Ready 状态及扩缩容变化;
- Gateway Integration:声明与 EPP 的关联,使 Gateway/Proxy 能够在转发请求前获得 EPP 的 Endpoint 选择结果。
需要注意,InferencePool 本身不执行推理,也不是请求必须“经过”的数据面代理。它更接近一份 可调度 Endpoint 集合的声明与发现机制。
2.3 Proxy 与 EPP
Proxy 与 EPP 的职责边界可以概括为:
- Proxy 负责转发请求;
- EPP 负责决定请求发往哪里。
当请求进入 Gateway/Proxy 后,Proxy 会将调度决策交给 EPP。EPP 从 InferencePool 对应的候选 Endpoint 中选择一个目标,随后 Proxy 再将原始请求转发至该 Model Server。
这使得数据转发与调度策略相互解耦:Proxy 无需理解 LLM 的 Queue、KV Cache 或 Prefix Cache,只需要执行 EPP 返回的路由决策。
3. EPP:面向推理请求的 Scheduler
EPP(Endpoint Picker)可以理解为 llm-d 的 Request Scheduler。
如果将 Kubernetes Scheduler 的职责概括为:
1 | Pod -> Node |
那么 EPP 的职责可以概括为:
1 | Inference Request -> Model Server Endpoint |
EPP 的核心调度模型采用:
1 | Filter -> Score -> Pick |
对应流程如下:
%%{init: {"look": "handDrawn", "theme": "base", "handDrawnSeed": 11}}%%
flowchart LR
R["Inference Request"]
C["Candidate Endpoints"]
subgraph SP["SchedulingProfile"]
direction LR
F[Filters] --> S["Weighted Scorers"] --> P[Picker]
end
R --> F
C --> F
P --> O["Selected Endpoint"]
classDef input fill:#EEE8FF,stroke:#7663C6,stroke-width:1.5px,color:#222
classDef stage fill:#D8F5F2,stroke:#3A9C96,stroke-width:1.5px,color:#222
classDef result fill:#FFF2BF,stroke:#B9952A,stroke-width:1.5px,color:#222
class R,C input
class F,S,P stage
class O result
3.1 Filter:确定“谁有资格”
Filter 用于缩小候选 Endpoint 集合。
例如,在不同场景中可以根据以下条件过滤:
- Endpoint 的角色或 Label;
- SLO Headroom;
- Prefix Cache Affinity;
- Prefill / Decode 角色。
Filter 的目标不是判断哪个 Endpoint 最优,而是先排除当前请求不应选择的候选对象。
3.2 Score:确定“谁更合适”
通过 Filter 的 Endpoint 会进入 Scoring 阶段。EPP 可以组合多个 Scorer,例如:
- Queue Depth;
- Running Requests;
- Token Load;
- KV Cache Utilization;
- Prefix Cache Match;
- Predicted Latency。
不同 Scorer 可以配置不同权重,最终形成 Endpoint 的综合分数。
3.3 Pick:确定最终 Endpoint
Picker 根据评分结果选择最终 Endpoint。最常见的策略是选择最高分 Endpoint,也可以采用 Weighted Random 等方式降低大量请求同时集中到单个最高分 Endpoint 的风险。
因此,EPP 并不是固定的一套“最短队列算法”,而是一套可组合的 Request Scheduling Framework。
4. 智能路由:从 Load-Aware 到 KV Cache-Aware
传统 L7 Load Balancer 通常依据连接数、请求数或基础资源利用率进行流量分配。LLM Serving 的请求成本差异显著,并且不同 Endpoint 之间还存在 KV Cache Locality,因此仅使用传统指标不足以得到稳定的调度结果。
llm-d 的智能路由将 负载状态 与 KV Cache Locality 同时纳入调度决策。
4.1 Load-Aware Routing:识别真实 Serving 压力
传统 Round Robin 隐含了一个假设:不同请求的处理成本大致接近。但这一假设在 LLM Serving 中并不成立。
例如:
1 | Request A: 500 input tokens |
从 HTTP 层面看,两者都只是一个请求;从 GPU 计算量和 KV Cache 占用来看,两者的成本可能相差巨大。
因此,仅按照请求数量做均衡容易出现如下情况:
1 | Pod A: 少量长请求,实际负载很高 |
llm-d 可以利用 Model Server 暴露的运行状态进行 Load-Aware Routing,包括:
- Queue Depth:等待处理的请求数量;
- Running Requests:当前正在执行的请求数量;
- Token Load:Endpoint 正在承担的 Token 工作量;
- KV Cache Utilization:KV Cache 的占用压力。
其目标并不是简单选择“请求数最少”的 Endpoint,而是尽量将请求分配给具有更多实际 Serving Headroom 的实例。
4.2 Prefix / KV Cache-Aware Routing:利用 Cache Locality
Load-Aware Routing 解决的是“哪个 Endpoint 更空闲”,而 LLM Routing 还有另一个特殊维度:KV Cache Locality。
假设多个 Endpoint 均可以处理同一个模型:
1 | Pod A: 已缓存当前请求的大部分 Prefix KV |
如果将请求发送至 Pod A,就可以复用已有 Prefix KV,减少重复 Prefill;如果发送至 Pod C,则可能需要重新计算完整 Prompt。
因此,Prefix Cache-Aware Routing 的目标是:
在满足负载与服务质量约束的前提下,优先选择已经拥有更多可复用 Prefix KV 的 Endpoint。
其直接收益通常体现在减少重复 Prefill、降低 TTFT,并提升 GPU 的有效计算比例与整体吞吐。
但 Prefix Locality 不能成为唯一调度依据。如果某个 Endpoint 的 Cache Match 很高,但其 Queue 已严重堆积,继续将请求集中到该实例会形成热点。因此实际调度通常需要同时考虑:
1 | Cache Locality + Current Load |
这也是 llm-d 将 Prefix Cache Scorer 与 Queue、Token Load、KV Cache Utilization 等 Scorer 组合使用的原因。
4.3 Prefix Routing 的两种实现:Approximate 与 Precise
为了判断“某段 Prefix KV 位于哪个 Endpoint”,llm-d 提供两种不同实现路径。
4.3.1 Approximate Prefix Routing
Approximate 模式由 EPP 根据历史路由行为维护近似 Prefix 状态。
1 | 某个 Prefix 刚刚被路由到 Pod A |
这种方式的优势是轻量,不依赖 Model Server 主动汇报 KV Cache 变化。
其局限在于,EPP 的推测可能与真实状态发生偏离。例如 Pod A 已经因为内存压力淘汰了部分 KV Block,但 EPP 尚未感知这一变化。
4.3.2 Precise Prefix Routing
Precise 模式不再依赖“曾经把请求路由到哪里”的推测,而是使用 Model Server 实际产生的 KV Cache 状态变化事件维护缓存位置视图。
它依赖两个关键能力:
- KVEvents:Model Server 对 KV Cache 状态变化发出的元数据事件;
- KV Cache Indexer:消费这些事件并维护 KV Block 到 Endpoint 的位置索引。
与 Approximate 模式相比,Precise 模式能够更直接地反映 Model Server 当前的真实缓存状态。其完整的数据流将在后续 KVEvents 与 KV Cache Indexer 小节中展开。
4.4 KVEvents:KV Cache 的状态变化通知
KVEvents 并不是 KV 数据传输协议,也不会将真正的 K/V Tensor 发送给 EPP。
它更接近 Model Server 对自身 KV Cache 状态变化发布的元数据事件,例如:
BlockStored:新的 KV Block 被缓存;BlockRemoved:某个 KV Block 被淘汰;AllBlocksCleared:当前实例的 KV Cache 被整体清空。
真正的 KV Tensor 仍然位于 Model Server 所管理的 GPU、CPU 或其他 Cache Tier 中。
因此可以将两者区分为:
1 | KV Cache = 真正的推理数据 |
vLLM、SGLang 等 Model Server 可以提供 KVEvents 能力,llm-d 则利用这些事件建立全局缓存位置视图。
4.5 KV Cache Indexer:从事件流构建全局 KV 视图
KV Cache Indexer 位于 EPP 内部,其核心数据结构可以抽象为:
1 | Block Key -> Endpoints |
例如:
1 | Block A -> Pod 1, Pod 3 |
Indexer 同时承担两类工作:一类是持续消费 Model Server 的 KVEvents,更新缓存位置索引;另一类是在请求到达时,根据 Prefix 对索引进行查询,为调度器生成每个 Endpoint 的 Prefix Match 信息。
%%{init: {"look": "handDrawn", "theme": "base", "handDrawnSeed": 23}}%%
flowchart TB
M["Model Servers<br/>vLLM / SGLang"] -->|"Write Path: KVEvents"| I[("KV Index<br/>block key -> endpoints")]
R["Inference Request"] --> T["Token Producer"]
T --> P["Precise Prefix Cache Producer"]
P <-->|"Read Path: block lookup"| I
P --> MI["PrefixCacheMatchInfo"]
MI --> S["Prefix Cache Scorer"]
classDef source fill:#EEE8FF,stroke:#7663C6,stroke-width:1.5px,color:#222
classDef control fill:#D8F5F2,stroke:#3A9C96,stroke-width:1.5px,color:#222
classDef data fill:#FFF2BF,stroke:#B9952A,stroke-width:1.5px,color:#222
class M,R source
class T,P,S control
class I,MI data
4.5.1 Write Path:持续更新索引
Model Server 的 KV Cache 状态不断变化。每当 Block 被写入、淘汰或整体清空时,相应 KVEvents 会更新 Index。
例如:
1 | Pod A: BlockStored(X) |
Write Path 的目标是让索引持续接近当前真实缓存状态。
4.5.2 Read Path:为当前请求查询 Cache Locality
当新请求到达后,Token Producer 先得到请求对应的 Token 序列,Precise Prefix Cache Producer 再构造可查询的 Block Key,并对 Index 执行查找,计算各 Endpoint 能够复用的连续 Prefix。
1 | Request Prefix: |
这里关注的是 连续 Prefix,而不是任意 Block 的命中数量。由于自回归模型具有因果依赖,后续 KV Block 无法脱离前面的 Prefix 链独立复用。
Indexer 最终提供的是缓存位置与 Prefix Match 信息,而不是最终路由结果。真正的 Endpoint 选择仍由 EPP Scheduler 综合其他信号完成:
1 | KV Cache Indexer |
4.6 最终路由决策:将多个信号汇入 EPP
前面的 Load-Aware、Prefix Cache-Aware 与 SLO 相关能力并不是彼此独立的路由器,而是共同为 EPP 的 SchedulingProfile 提供决策依据。不同信号可能进入 Filter,也可能进入 Scorer,最终统一经过 Pick 阶段得到目标 Endpoint。
%%{init: {"look": "handDrawn", "theme": "base", "handDrawnSeed": 31}}%%
flowchart LR
R["Request Context<br/>model / objective / prompt"]
L["Load Signals<br/>queue / running / token load / KV util"]
K["Cache Signals<br/>prefix match / cache locality"]
O["SLO / Policy<br/>latency headroom / role constraints"]
E["EPP<br/>SchedulingProfile"]
T["Selected Endpoint"]
M["Model Server"]
R --> E
L --> E
K --> E
O --> E
E --> T --> M
classDef input fill:#EEE8FF,stroke:#7663C6,stroke-width:1.5px,color:#222
classDef control fill:#D8F5F2,stroke:#3A9C96,stroke-width:1.5px,color:#222
classDef result fill:#FFF2BF,stroke:#B9952A,stroke-width:1.5px,color:#222
class R,L,K,O input
class E control
class T,M result
传统负载均衡主要关注连接数、请求数或基础资源利用率;llm-d 则进一步将 LLM Serving 特有的信息纳入请求调度,包括 Endpoint 当前负载、Token 工作量、KV Cache 压力、Prefix Cache Locality,以及请求对应的 SLO 或角色约束。
因此,llm-d Router 的核心价值并不是提供另一套通用 L7 负载均衡,而是把推理系统内部状态转化为可用于 Request Scheduling 的信号。
5. 总结
本文可以归纳为三层逻辑:
1 | 第一层:Endpoint Discovery |
其中,KV Cache Indexer 又将单实例内部的 Prefix Cache 能力扩展为可供 Router 使用的全局 Cache Locality 信息:
1 | Model Server KV Cache |
这构成了 llm-d 上半部分最核心的架构闭环:发现可用 Endpoint、理解 Endpoint 状态,并基于负载与 KV Cache Locality 对请求进行智能调度。
下篇将在此基础上继续讨论:
- Prefill / Decode Disaggregation;
- P/D 请求编排与 KV Transfer;
- KV Offloading 与 P2P Prefix Cache Sharing;
- Autoscaling 与 SLO 驱动的容量管理;
- Batch / Async Inference。







