概述#

LMCache multiprocess (MP) mode runs LMCache as a standalone service that vLLM instances reach through a configurable ZMQ or gRPC request transport. One LMCache server per node can serve multiple vLLM pods, providing process isolation, shared caching, and independent resource scaling.

主要优势#

  • 进程隔离 -- LMCache 和 vLLM 在独立的进程(或容器)中运行,因此与缓存相关的问题不会导致推理引擎崩溃。

  • 推理路径上没有 GIL 争用或 Python 开销 -- 通过在单独的进程中运行 LMCache,它的 Python GIL 和 CPU 工作(哈希、内存管理、L2 I/O)不会与 vLLM 的推理线程竞争。

  • 跨 Pod 共享缓存 -- 同一节点上的多个 vLLM 实例共享一个 L1 缓存,最大化 KV 重用。

  • 独立资源扩展 -- 为缓存分配 CPU 内存,与用于推理的 GPU 显存独立。

  • 多层存储 (L1 + L2) -- L1 缓存(在 CPU DRAM 中,或通过 GPUDirect Storage 的 NVMe 块),由通过 NIXL(GDS、POSIX、HF3FS 等)的持久化 L2 存储提供支撑。

  • 内置可观察性 -- 开箱即用的 Prometheus 指标和遥测事件系统。

前提条件#

  • vLLM 建议使用最新版本以获得最佳兼容性

  • LMCache 最新开发分支

服务器变体#

LMCache 提供两个服务器入口点:

入口点

描述

lmcache server

Recommended. Configurable request transport (ZMQ by default, or gRPC) + FastAPI HTTP frontend — see HTTP API.

python3 -m lmcache.v1.multiprocess.server

(Legacy) Request server with no HTTP endpoints; same --engine-type / --supported-transfer-mode flags as lmcache server. Prefer lmcache server.

以下部分介绍 LMCache MP 模式的内部实现,适合希望了解、调试或扩展该系统的读者。

高层架构#

Engine Worker(s)
     |
     | RequestClient (URL scheme selects ZMQ or gRPC)
     v
RequestServer factory (transport/server_factory.py)
     |                                      |
     | ZMQ                                  | gRPC
     v                                      v
MessageQueueServer                  GrpcMultiprocessServer
(transport/zmq_impl/mq.py)          (transport/grpc_impl/server.py)
      |                                      |
      +------------------+-------------------+
                        | dispatch by operation name
                        v
EngineModule handlers owned by MPCacheServer (server.py)
     |
     |--- TokenHasher / SessionManager
     |
     v
StorageManager (distributed/storage_manager.py)
     |
     |--- L1Manager (l1_manager.py)
     |       |--- L1MemoryManager (CPU DRAM),
     |       |    DevDaxL1MemoryManager (Device-DAX slab), 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)

引擎和模块#

所有服务器入口点共享相同的 MPCacheServerStorageManager 核心。MPCacheServer 现在是一个轻量级合成器:它持有一个 MPCacheServerContext 和一个由 _build_modules()(在 server.py 中)基于 --engine-type--supported-transfer-mode 组装的 EngineModule 实例列表。

``server.py`` -- The transport-neutral server compositor. Creates an MPCacheServer, assembles the engine modules (LookupModule + ManagementModule + LMCacheDrivenTransferModule and/or EngineDrivenTransferModule depending on --supported-transfer-modelmcache_driven (default) or engine_driven loads just one, auto loads both — plus the blend module when --engine-type blend is set). It calls create_request_server() to build the ZMQ or gRPC request server selected by --transport, discovers the annotated operations exposed by the loaded modules, and blocks in a keep-alive loop.

``modules/blend.py`` -- Defines BlendModule, the paged-aware blend pipeline that enables non-prefix KV cache reuse (e.g. across document paragraphs) on the sparse-prefetch path. KV-cache registration rides the standard REGISTER_KV_CACHE; the module adds only the CB RPCs (CB_REGISTER_ROPE, CB_UNREGISTER_ROPE, CB_RETRIEVE_PRE_COMPUTED, CB_UNIFIED_LOOKUP) and wraps STORE to register chunk fingerprints, reusing the existing LMCacheDrivenTransferModule and LookupModule. Selected by passing --engine-type blend to lmcache server; requires --supported-transfer-mode to be lmcache_driven or auto and refuses to load when it is engine_driven.

``http_server.py`` -- Wraps run_cache_server() (from server.py) inside a FastAPI application. Endpoints are contributed by modules under http_apis/ and auto-registered via HTTPAPIRegistry: GET / (basic liveness), GET /healthcheck for Kubernetes probes, POST /cache/clear for clearing all KV cache data in L1 (CPU) memory, and GET /status for inspecting detailed internal state. The selected request server runs as part of the same process, and any configured runtime plugins are spawned by MPRuntimePluginLauncher during FastAPI startup.

Request Operations#

Workers call the same typed operations through either ZMQ (DEALER/ROUTER) or gRPC. The handler scheduling contract is transport-neutral.

ZMQ encodes requests as msgspec multipart messages over DEALER/ROUTER sockets; gRPC encodes the same operations with protobuf. Both dispatch by operation name; see Request Transport for endpoint selection and wire details.

RPC operations (declared by typed methods on RequestClient):

请求类型

处理程序类型

描述

REGISTER_KV_CACHE

同步

为 vLLM 实例注册 GPU KV Cache 张量。

UNREGISTER_KV_CACHE

同步

注销 KV Cache 张量。

REGISTER_KV_CACHE_ENGINE_DRIVEN_CONTEXT

同步

注册一个引擎驱动的 KV Cache 上下文(使用 PREPARE/COMMIT 传输路径的 CPU/加速器工作线程)。仅在 --supported-transfer-modeengine_drivenauto 时加载。当使用 SHM 路径时,返回一个 RegisterEngineDrivenContextResponse,携带 SHM 段名称和池大小(对于 pickle 路径为空)。

UNREGISTER_KV_CACHE_ENGINE_DRIVEN_CONTEXT

同步

注销引擎驱动的 KV Cache 上下文。

STORE

阻塞

将 KV Cache 块从 GPU 存储到 L1 (CPU)。基于 LMCache 的传输路径 (CUDA IPC);仅在 --supported-transfer-modelmcache_drivenauto 时加载。

RETRIEVE

阻塞

将 KV Cache 块从 L1 (CPU) 复制回 GPU。由 LMCache 驱动的传输路径 (CUDA IPC);仅在 --supported-transfer-modelmcache_drivenauto 时加载。

PREPARE_STORE

阻塞

(引擎驱动路径)工作线程请求服务器为一个键准备存储端传输状态。当 --supported-transfer-mode 设置为 engine_drivenauto 时加载。

COMMIT_STORE

阻塞

(引擎驱动路径)工作线程提交块的序列化字节(pickle 路径),或释放已准备好的共享内存槽(SHM 路径),以便服务器将数据持久化到 L1 存储。

PREPARE_RETRIEVE

阻塞

(引擎驱动路径)工作线程请求服务器为指定键准备检索负载。pickle 路径直接内联返回字节;SHM 路径返回槽位信息,供工作线程从共享内存中读取。

COMMIT_RETRIEVE

阻塞

(引擎驱动路径)工作线程确认检索完成,服务器随即释放底层读取锁并回收相关传输状态。

LOOKUP

阻塞

提交前缀查找;预取任务由 request_id 在服务器端跟踪。

QUERY_PREFETCH_STATUS

阻塞

通过 request_id 查询预取作业。当完成时返回已加载的块数,预取仍在进行中时返回 None

WAIT_PREFETCH_STATUS

阻塞

(仅限 SGLang)阻塞直到预取作业完成,然后返回其加载的块数,超时则返回 None。这是轮询 QUERY_PREFETCH_STATUS 的阻塞替代方案。

QUERY_PREFETCH_LOOKUP_HITS

阻塞

在预取完成之前,通过 request_id 查询查找阶段的命中块计数。当查找仍在运行时返回 None

FREE_LOOKUP_LOCKS

阻塞

从取消的查找中释放读取锁,而无需执行完整的检索。

END_SESSION

阻塞

移除已完成请求的会话状态。

CLEAR

阻塞

Clear cached data. The optional force flag defaults to False; when set, active locks may be ignored.

GET_CHUNK_SIZE

同步

返回服务器的块大小。

PING

阻塞

存活 ping;处理程序始终返回 True

REPORT_BLOCK_ALLOCATION

阻塞

vLLM 调度器的即发即忘通道,用于向可观察性子系统上报 GPU 块分配事件。

NOOP

同步

调试心跳 -- 返回确认字符串。

CB_REGISTER_ROPE

同步

(Blend) Share the RoPE cos/sin cache onto a context already registered via REGISTER_KV_CACHE.

CB_UNREGISTER_ROPE

同步

(Blend) Drop the RoPE state (paged KV cache lives on; use UNREGISTER_KV_CACHE to release that).

CB_RETRIEVE_PRE_COMPUTED

阻塞

(Blend) Scatter all matched chunks (prefix- and non-prefix-hit) into paged KV by per-token block ID; re-RoPE only the shifted subset.

CB_UNIFIED_LOOKUP

阻塞

(Blend) Sole lookup path: one RPC runs prefix + non-prefix match, reconciles, issues one sparse-coalesced prefetch, and classifies per-TP-rank. Returns CBUnifiedLookupResult (or None while the prefetch is still in flight).

P2P_LOOKUP_AND_LOCK

阻塞

(P2P)查找给定的键并对本地缓存的前缀进行读锁。返回一个任务 ID,调用者将其传递给 P2P_QUERY_LOOKUP_RESULTS 以轮询传输地址。由 P2PController 提供服务(由 _build_modules() 无条件加载);该服务器是否还充当 P2P 客户端由 --p2p-advertise-url 控制 -- 参见 P2P KV 缓存共享

P2P_QUERY_LOOKUP_RESULTS

阻塞

(P2P) 查询查找任务的传输地址。查找完成后返回一个 TransferChannelAddress 列表,或者在查找仍在进行中或其结果已被消费时返回 None

P2P_UNLOCK_OBJECTS

阻塞

(P2P) 释放之前由 P2P_LOOKUP_AND_LOCK 在给定键上获取的读锁。

处理程序类型:

  • SYNC -- Serialized by the request transport (the ZMQ main loop or the gRPC sync-handler lock) for fast, non-blocking work.

  • BLOCKING -- Dispatched to a normal or client-affinity thread pool (may involve GPU copies or I/O).

配置系统#

每个配置模块都暴露一个可组合的三元组:

(DataclassConfig, add_*_args(parser), parse_args_to_*_config(args))

server.py:parse_args() 组合它们:

parser = argparse.ArgumentParser(...)
add_mp_server_args(parser)        # from multiprocess/config.py
                                  # includes runtime-plugin args
                                  # (--runtime-plugin-locations,
                                  #  --runtime-plugin-config)
add_storage_manager_args(parser)  # from distributed/config.py
  # which internally calls add_l2_adapters_args(parser)
add_observability_args(parser)    # from mp_observability/config.py

http_server.py reuses this pattern, adding add_http_frontend_args() and add_coordinator_args() for the lmcache server CLI. CacheBlend is no longer a separate entry point — it is opted into at runtime by passing --engine-type to server.py (or lmcache server): --engine-type blend appends BlendModule.

分布式存储#

StorageManager#

lmcache/v1/distributed/storage_manager.py

将 L1、L2 和所有控制器连接在一起的顶级管理器。关键方法:

  • reserve_write() / finish_write() -- 对 L1 的两阶段写入。

  • submit_prefetch_task() / query_prefetch_status() -- 异步查找 + L2 预取。 query_prefetch_status() 是非阻塞的,并在 L2 预取仍在进行时返回 None

  • wait_prefetch_status() -- 轮询 query_prefetch_status() 的阻塞替代方案:在预取结果发布(或超时到期)之前,等待控制器条件变量。仅 L1 的预取会立即返回。由 WAIT_PREFETCH_STATUS RPC 使用,以避免在加载路径上进行忙等待。

  • read_prefetched_results() / finish_read_prefetched() -- 从 L1 读取预取的数据,并自动管理锁。

L1Manager#

lmcache/v1/distributed/l1_manager.py

在 CPU 内存中使用状态机管理对象:

None --> write_locked --> ready --> read_locked
          (reserve_write)  (finish_write)  (reserve_read)
                              |                |
                              v                v
                           evictable      finish_read -> ready

每个对象都有两个 TTLLock 实例(读和写),并具有可配置的超时,以防止因崩溃的客户端导致的死锁。

The underlying memory allocation is handled by one of three interchangeable tiers selected at startup (all satisfy L1ManagerProtocol):

  • L1MemoryManager(默认)-- 固定 CPU DRAM,按需惰性增长至 --l1-size-gb

  • DevDaxL1MemoryManager -- a Device-DAX-backed L1 slab when --l1-devdax-path is set. A pure Device-DAX configuration maps the DAX device as the full L1 arena; a hybrid configuration uses DRAM first and spills overflow allocations into Device-DAX. See the L1 Memory Manager section of 配置参考 for the accepted knobs.

  • GDSL1MemoryManager -- an NVMe slab file when --gds-l1-path is set. The bytes live on disk; reads/writes DMA directly between the GPU staging buffer and the slab, driven by the process-global GDSContext (gpu_connector/gds_context.py) and dispatched from gpu_ops. The DMA backend is selected by platform via gpu_connector/_gds_backends.py -- cuFile (libcufile.so) on NVIDIA and hipFile (libhipfile.so) on AMD ROCm; see the GDS L1 Tier section of 配置参考 for the vendor-specific requirements. The CPU tier is disabled in this mode.

L2 适配器#

lmcache/v1/distributed/l2_adapters/

L2AdapterInterface(在 base.py 中)定义了三个异步任务方法:

  • submit_store_task(key, data) -- 将数据推送到 L2。

  • submit_lookup_and_lock_task(keys) -- 检查键是否存在于 L2 中。

  • submit_load_task(keys, layout_desc) -- 从 L2 加载数据到 L1。

工厂函数 create_l2_adapter()(位于 __init__.py)通过对配置类型执行 isinstance() 检查,实例化对应的适配器。

新适配器类型通过 register_l2_adapter_type()config.py 中注册。

控制器#

StoreController (storage_controllers/store_controller.py):事件驱动的后台线程,使用 select.poll() 监听 listener eventfd 和各适配器的存储 eventfd。当 L1 中出现新对象(通过 StoreListener 发信号)时,根据 StorePolicy 向每个 L2 适配器提交异步存储任务。

EvictionController (storage_controllers/eviction_controller.py): Periodically checks L1 memory usage against the watermark threshold. When triggered, evicts objects using the configured policy (LRU, ARC, IsolatedLRU, or noop) until usage drops below the target. IsolatedLRU evicts per cache_salt against limits registered through the /quota HTTP endpoints; see 配额管理.

PrefetchController (storage_controllers/prefetch_controller.py):处理 StorageManagerLOOKUP RPC 期间提交的 L2 查找和加载请求。当键不在 L1 中时,查询 L2 适配器并将命中的数据加载回 L1。

请求流#

查找流程#

vLLM                MPCacheServer          StorageManager         L1Manager       L2 (PrefetchController)
 |                       |                       |                    |                    |
 |---LOOKUP(key)-------->|                       |                    |                    |
 |                       |--submit_prefetch------>|                    |                    |
 |                       |                       |--reserve_read----->|                    |
 |                       |                       |<--hit_count--------|                    |
 |                       |                       |--submit_prefetch_request--------------->|
 |                       |                       |    (remaining keys)                     |
 |                       |--query_prefetch------->|                    |                    |
 |                       |                       |--query_prefetch_result----------------->|
 |                       |<--found_count----------|                    |                    |
 |<--found_count---------|                       |                    |                    |

存储流程#

vLLM                MPCacheServer          StorageManager         L1Manager
 |                       |                       |                    |
 |---STORE(key,blocks)-->|                       |                    |
 |                       |--reserve_write-------->|                    |
 |                       |                       |--reserve_write---->|
 |                       |                       |<--memory_objs------|
 |                       |  (GPU->CPU copy)      |                    |
 |                       |--finish_write--------->|                    |
 |                       |                       |--finish_write----->|
 |                       |                       |                    |
 |                       |                       |  [StoreController detects new objects]
 |                       |                       |  [async L1->L2 push via adapters]
 |<--event_handle--------|                       |                    |

获取流程#

vLLM                MPCacheServer          StorageManager         L1Manager
 |                       |                       |                    |
 |---RETRIEVE(key)------>|                       |                    |
 |                       |--read_prefetched------>|                    |
 |                       |                       |--unsafe_read------>|
 |                       |                       |<--memory_objs------|
 |                       |  (CPU->GPU copy)      |                    |
 |                       |--finish_read_prefetch->|                    |
 |                       |                       |--finish_read------>|
 |<--event_handle--------|                       |                    |

可观察性内部实现#

EventBus (lmcache/v1/mp_observability/event_bus.py) 是一个全局单例,在服务器启动时由 init_observability() 初始化。生产者(L1Manager、StorageManager、MPCacheServer)将 Event 对象发布到有界队列(--event-bus-queue-size,默认 10000,溢出时尾部丢弃)。一个后台排空线程(drain thread)将每个事件分发给所有已注册的订阅者。

订阅者 位于 lmcache/v1/mp_observability/subscribers/ 下,按关注点分组:metrics/(OTel 计数器和生命周期直方图)、logging/(Python 日志处理器、查找哈希 JSONL)以及 tracing/(由 START/END 事件对构建的 OTel span)。init_observability() 根据 CLI 标志(--disable-metrics--disable-logging--enable-tracing)注册相应的订阅者集合。

OTel 提供者在构造订阅者之前通过 otel_init.py 完成初始化,确保模块级的 get_meter() / get_tracer() 调用绑定到真实的提供者。指标同时导出到进程内的 Prometheus /metrics 端点(--prometheus-port,默认 9090),并在设置了 --otlp-endpoint 时推送到 OTel 收集器。

如何扩展#

添加新的 L2 适配器#

lmcache/v1/distributed/l2_adapters/ 下创建一个新的 *_l2_adapter.py 模块 — __init__.py 会通过 pkgutil 自动发现匹配该后缀的模块,并在首次使用时惰性导入,无需修改其他文件。

  1. 创建一个配置类,继承自 L2AdapterConfigBase,并实现 from_dict()help() 方法。

  2. 创建一个实现 L2AdapterInterface 的适配器类,以及一个小型工厂函数 (config, l1_memory_desc) -> L2AdapterInterface

  3. 在模块级别,自注册配置和工厂:

    register_l2_adapter_type("my_adapter", MyAdapterConfig)
    register_l2_adapter_factory("my_adapter", _create_my_adapter)
    

请参阅 mock_l2_adapter.pys3_l2_adapter.py 以获取参考实现。

添加可观察性订阅者#

  1. 创建一个继承自 EventSubscriber 的订阅者类(定义在 lmcache/v1/mp_observability/event_bus.py 中):实现 get_subscriptions() 以返回 {EventType: callback} 映射;可选地重写 shutdown() 以进行清理。

  2. 将类放置在对应的关注组(subscribers/metrics/subscribers/logging/subscribers/tracing/)下,并从该包的 __init__.py 中导出。

  3. init_observability()lmcache/v1/mp_observability/config.py)中,通过 bus.register_subscriber(...) 在与其关注点(metrics / logging / tracing)匹配的分支内注册订阅者,如有需要可受相应 CLI 标志控制。

Adding a new RPC#

  1. Add a typed @rpc_method to RequestClient.

  2. Add the gRPC protobuf request, response, and service method.

  3. Add a same-named @request_handler on the appropriate EngineModule.

Existing ZMQ operations retain their frozen numeric wire IDs. New operations use their string name and do not extend the legacy compatibility table.

关键源文件#

文件

目的

lmcache/v1/multiprocess/server.py

MPCacheServer composition and transport-neutral request-server entry point

lmcache/v1/multiprocess/config.py

MPServerConfig, HTTPFrontendConfig

lmcache/v1/multiprocess/engine_context.py

MPCacheServerContext(传递给每个 EngineModule 的共享状态)

lmcache/v1/multiprocess/engine_module.py

Transport-neutral EngineModule protocol

lmcache/v1/multiprocess/rpc.py

RPC discovery and typed operation specifications

lmcache/v1/multiprocess/transport/base.py

Typed RequestClient and RequestServer contracts

lmcache/v1/multiprocess/transport/server_factory.py

Transport-neutral request-server construction boundary

lmcache/v1/multiprocess/transport/zmq_impl/server.py

ZMQ HandlerSpec and ThreadPoolType definitions, per-module handler adapters, and message queue server construction

lmcache/v1/multiprocess/transport/grpc_impl/protos/

Protobuf wire contracts for the gRPC request transport

lmcache/v1/multiprocess/transport/grpc_impl/_proto_gen/

Build-time protobuf generator and generated Python package

lmcache/v1/multiprocess/transport/grpc_impl/server.py

gRPC service binding, handler scheduling, and request-server construction

lmcache/v1/multiprocess/modules/

Engine module implementations: lookup.py (LookupModule), management.py (ManagementModule), lmcache_driven_transfer.py (LMCacheDrivenTransferModule), engine_driven_transfer.py (EngineDrivenTransferModule), and blend.py (BlendModule, the paged-aware blend pipeline selected by --engine-type blend).

lmcache/v1/multiprocess/http_server.py

带健康检查和许多其他有用 API 的 FastAPI 包装器

lmcache/v1/multiprocess/http_api_registry.py

HTTPAPIRegistry 会自动发现 http_apis/ 中的路由器

lmcache/v1/multiprocess/http_apis/

可扩展的 HTTP 端点 (/, /healthcheck, /cache/clear, /status)

lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py

MPRuntimePluginLauncher 通过将完整的服务器配置序列化为环境变量来生成运行时插件

lmcache/v1/distributed/storage_manager.py

存储管理器(顶级管理器)

lmcache/v1/distributed/config.py

StorageManagerConfig 层次结构

lmcache/v1/distributed/l1_manager.py

L1Manager(对象状态机)

lmcache/v1/distributed/l2_adapters/config.py

L2 适配器配置注册表

lmcache/v1/distributed/l2_adapters/base.py

L2AdapterInterface

lmcache/v1/distributed/storage_controllers/store_controller.py

StoreController(事件驱动 L1->L2)

lmcache/v1/distributed/storage_controllers/eviction_controller.py

逐出控制器(基于水印触发)

lmcache/v1/distributed/storage_controllers/prefetch_controller.py

PrefetchController (L2->L1 未命中时)

lmcache/v1/mp_observability/config.py

可观察性配置 + init_observability() 入口点

lmcache/v1/mp_observability/event_bus.py

事件总线单例和 EventSubscriber 基类

lmcache/v1/mp_observability/event.py

Event / EventType 定义

lmcache/v1/mp_observability/otel_init.py

OTel 指标 / 跟踪提供程序设置

lmcache/v1/mp_observability/subscribers/

指标、日志和追踪订阅者

lmcache/v1/mp_observability/trace/

追踪记录 (--trace-level storage) 捕获堆栈