HTTP API#
当 MP 服务器通过 lmcache server 启动时(推荐入口点),会同时暴露一个基于 FastAPI 的 HTTP 前端和 vLLM 使用的 ZMQ 套接字。此 HTTP API 面向运维人员、编排工具(例如 Kubernetes)和调试工具——它不位于推理数据路径上。
路由的来源#
路由由三个来源组装而成,在启动时由 HTTPAPIRegistry 合并为一个 FastAPI 应用:
MP 原生路由 — 在
lmcache/v1/multiprocess/http_apis/下任何名为*_api.py的模块,如果暴露了模块级的router(一个fastapi.APIRouter),将会被自动发现。这涵盖了运维操作面:状态、缓存控制、L2 管理、配额和运行时重新配置。共享“公共”路由 —
lmcache/v1/multiprocess/http_apis/common_api.py聚合了lmcache/v1/internal_api_server/common/下的每个兼容的 router(跳过任何列在_MP_INCOMPATIBLE_MODULES中的模块,目前为空),并将它们转发到自动发现流水线。这些是与嵌入 vLLM 的 API 服务器共享的跨服务器诊断(/env、/loglevel、/metrics、/threads、/periodic-threads*、/run_script)。在internal_api_server/common下添加新的兼容模块无需在 MP 端进行任何改动。重新导出的版本路由 — 基本信息组
lmcache/v1/multiprocess/http_apis/info_api.py包含来自lmcache/v1/internal_api_server/vllm/version_api.py的路由器,暴露/version、/lmc_version和/commit_id,以及/、/healthcheck和/status。
服务器配置#
参数 |
默认 |
描述 |
|---|---|---|
|
|
绑定 HTTP 服务器的主机。 |
|
|
绑定 HTTP 服务器的端口。 |
示例:
lmcache server \
--l1-size-gb 100 --eviction-policy LRU \
--http-host 0.0.0.0 --http-port 8080
以下所有示例都假设服务器可以通过 http://localhost:8080 访问。
端点概述#
下面的路由按目的分组。操作接口(健康检查、状态、缓存和存储控制)位于顶层路径;从共享的 internal_api_server 包继承的路由保持其原始路径,以与嵌入的 vLLM API 服务器兼容。
备注
多个处理程序在响应 主体 中报告失败,而不是通过非 200 状态码(例如 DELETE /cache/objects 返回 200 和 ok=false,而 /periodic-threads-health 返回 200 和 healthy=false)。错误字段名称也不统一:/healthcheck 和 /cache/clear 在失败时使用 reason,而 /status、/config 和 /cache/checksums 使用 error。每个端点的详细信息如下是权威的。
存活性和健康
方法 |
路径 |
目的 |
|---|---|---|
GET |
|
静态存活检测(不触及引擎)。 |
GET |
|
K8s 存活/就绪探针; |
检查和状态
方法 |
路径 |
目的 |
|---|---|---|
GET |
|
用于检查和调试的详细引擎快照(L1、L2、注册的上下文、会话、预取作业)。 |
GET |
|
转储合并的服务器配置对象( |
GET |
|
列出活动缓存适配器( |
GET |
|
组合版本字符串 ( |
GET |
|
LMCache 包版本字符串。 |
GET |
|
构建提交 ID。 |
缓存管理
方法 |
路径 |
目的 |
|---|---|---|
GET |
|
对驻留在某个层/适配器中的对象进行分页(查询: |
DELETE |
|
Delete a caller-supplied list of object keys from L1, L2, or both (body:
|
POST |
|
通过从 L2 提前加载令牌序列来预热节点的 L1;返回 |
GET |
|
轮询已提交的热预取( |
POST |
|
强制清除某个层的常驻缓存(请求体: |
POST |
|
计算 KV Cache 块的 MD5 校验和(诊断 / 往返完整性检查)。 |
配额管理
方法 |
路径 |
目的 |
|---|---|---|
GET |
|
列出每个注册的 |
PUT |
|
设置或更新 |
GET |
|
读取单个 |
DELETE |
|
移除 |
运行时 L2 重新配置
方法 |
路径 |
目的 |
|---|---|---|
GET |
|
报告一个后端类型的可运行管理 L2 适配器。 (要发现可重新配置的后端,请使用 |
POST |
|
对后端适配器应用一个运行时重新配置操作。 |
可观察性
方法 |
路径 |
目的 |
|---|---|---|
GET |
|
Prometheus 展示格式。 |
POST |
|
将所有可观察性指标重置为初始状态。 |
诊断与调试
方法 |
路径 |
目的 |
|---|---|---|
GET |
|
列出或检查日志记录器级别;也接受 |
GET |
|
列出活动的 Python 线程及其堆栈跟踪。 |
GET |
|
列出注册的周期性线程及其摘要计数。 |
GET |
|
单个周期线程的详细状态。 |
GET |
|
对关键/高层周期线程的快速健康检查。 |
GET |
|
转储进程环境变量(JSON主体, |
POST |
|
在受限的沙箱中执行上传的 Python 脚本。 |
存活性和健康状态#
GET /#
基本的存活检查。返回一个静态负载,指示 HTTP 服务器正在运行;它不会触及缓存引擎。对于还验证引擎是否已初始化的探测,请使用 /healthcheck。
响应 (200 OK):
{
"status": "ok",
"service": "LMCache HTTP API"
}
HTTP 状态码:
200: 服务器正常运行(无条件;不影响引擎)。
示例:
curl -s http://localhost:8080/
GET /healthcheck#
健康检查端点适用于 Kubernetes 的存活和就绪探针。200 响应意味着 HTTP 服务器处于活动状态 并且 MP 缓存引擎对象已连接到 app.state。503 响应表示引擎尚未存在(仍在初始化中,或初始化失败)。该检查验证引擎属性是否已设置;它不会调用引擎以确认更深层次的存活状态。
响应 (200 OK):
{
"status": "healthy"
}
响应 (503 服务不可用):
{
"status": "unhealthy",
"reason": "engine not initialized"
}
HTTP 状态码:
200: 引擎已连接到app.state(正常)。503: 引擎未初始化(仍在启动中,或初始化失败)。
示例:
curl -s http://localhost:8080/healthcheck
Kubernetes 探针代码片段:
livenessProbe:
httpGet:
path: /healthcheck
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthcheck
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
检查和状态#
GET /status#
返回 MP 引擎内部状态的详细快照。有效负载由 MPCacheServer.report_status() 组装:一组固定的引擎级字段、完整的存储管理器状态,以及每个加载模块贡献的键(因此确切的键集取决于哪些模块处于活动状态 — registered_gpu_ids / cache_context_meta 来自传输模块,active_prefetch_jobs 来自查找模块,混合模式添加它们自己的字段)。旨在供操作员和调试使用,而非监控(使用 Prometheus 指标获取时间序列数据 — 参见 可观察性)。
响应 (200 OK):
{
"is_healthy": true,
"engine_type": "MPCacheServer",
"chunk_size": 256,
"hash_algorithm": "builtin-hash",
"active_sessions": 2,
"registered_gpu_ids": [0, 1],
"cache_context_meta": {
"0": {
"model_name": "meta-llama/Llama-3.1-8B-Instruct",
"world_size": 1,
"kv_cache_layout": {
"num_layers": 32,
"num_blocks": 12345,
"cache_size_per_token": 131072,
"kernel_groups": [
{
"kernel_group_idx": 0,
"engine_group_idx": 0,
"object_group_idx": 0,
"num_layers": 32,
"layer_indices": [0, 1, "..."],
"tokens_per_block": 16,
"slots_per_block": 16,
"dtype": "torch.bfloat16",
"engine_kv_concrete_shape": "...",
"is_mla": false,
"engine_kv_format": "...",
"engine_kv_shape": "...",
"attention_backend": "..."
}
]
}
}
},
"active_prefetch_jobs": 0,
"storage_manager": {
"is_healthy": true,
"...": "backend-specific fields"
}
}
响应 (503 服务不可用) 当引擎尚未初始化时:
{
"error": "engine not initialized"
}
HTTP 状态码:
200: 状态快照已返回。503: 引擎尚未在app.state上初始化。
示例:
curl -s http://localhost:8080/status | jq
GET /config#
以单一缩进 JSON 文档返回注册在 app.state.configs 上的全部服务器端配置对象(通常包括 mp、storage_manager 和 observability)。数据类通过 safe_asdict 序列化;其他值通过 make_json_safe 处理。可用于确认进程实际加载的配置内容(含环境变量覆盖),无需重启服务。
响应 (200 OK):
{
"mp": {
"http_host": "0.0.0.0",
"http_port": 8080,
"...": "..."
},
"storage_manager": {
"...": "..."
},
"observability": {
"...": "..."
}
}
响应(503 服务不可用),当配置尚未连接到 app.state 时:
{
"error": "configs not initialized"
}
HTTP 状态码:
200: 配置文档已返回。503: 配置尚未连接到app.state。
示例:
curl -s http://localhost:8080/config | jq
GET /config/adapters#
列出引擎已加载的每个 L2 适配器,按配置顺序排列。加载了哪些存储后端是配置检查的内容,因此它位于配置组中,而不是在 /cache 下。这是唯一的活动适配器列表;它取代了旧的 GET /reconfigure/backends``(可重新配置的后端是 ``type_name 值,其 reconfigurable 标志为 true)。
响应 (200 OK):
{
"adapters": [
{"index": 0, "type_name": "S3L2Adapter", "tier": "l2", "primary": true, "reconfigurable": false},
{"index": 1, "type_name": "dax", "tier": "l2", "primary": false, "reconfigurable": true}
]
}
primary 仅在第一个条目上为 true。reconfigurable 对于接受 /reconfigure 操作的适配器为 true — 将该适配器的 type_name 作为 {backend} 路径参数传递给 GET /reconfigure/{backend}/status 和重新配置操作。没有 L2 后端的引擎返回 {\"adapters\": []}``(仍然是 ``200 — 引擎已初始化,只是没有 L2 存储)。
HTTP 状态码:
200: 成功(包括无适配器的情况)。503: 引擎未初始化。
示例:
curl -s http://localhost:8080/config/adapters | jq
GET /version, GET /lmc_version, GET /commit_id#
版本描述符。每个返回一个裸 JSON 字符串(而不是对象):
GET /version— 来自lmcache.utils.get_version()的组合描述符,格式为\"<version>-<commit_id>\"(例如\"0.3.1-ca79ea33\")。在没有构建时版本元数据的源代码检出中,每个缺失的组件将回退为字面值\"NA\"(因此没有元数据的构建返回\"NA-NA\")。GET /lmc_version— 原始包版本字符串 (lmcache.utils.VERSION);当生成的lmcache._version模块缺失时返回空字符串\"\"。GET /commit_id— 构建中嵌入的 git 提交 ID (lmcache.utils.COMMIT_ID);当不可用时为空字符串\"\"。
这三者都是无条件的 200 OK。
响应 (200 OK):
"0.3.1-ca79ea33"
HTTP 状态码:
200: 返回的版本字符串(对所有三个路由无条件)。
示例:
curl -s http://localhost:8080/version
curl -s http://localhost:8080/lmc_version
curl -s http://localhost:8080/commit_id
缓存管理#
POST /cache/clear#
强制清除当前在某个层级(今天为 l1)中持有的 所有 KV 缓存数据。
警告
此端点为破坏性操作,会绕过读/写锁。正在进行的存储或预取操作可能因此损坏。仅在服务器空闲时,或在已知缓存状态异常需要恢复时使用。
请求体: 可选 -- 缺失(或空)体将使用以下默认值。
字段 |
类型 |
描述 |
|---|---|---|
|
字符串 |
可选(默认 |
|
布尔值 |
可选(默认 |
响应 (200 OK):
{"status": "ok", "cleared": {"tier": "l1"}}
HTTP 状态码:
200: 层已清除。400: 不支持的tier``(除了 ``l1以外的任何内容)。503: 服务器未初始化 ({\"detail\": \"服务器未初始化\"})。
示例:
# the body is optional; this clears the default tier (l1)
curl -s -X POST http://localhost:8080/cache/clear
POST /cache/checksums#
计算引擎 KV Cache 的 MD5 校验和,每个哈希块分组为 chunk_size 块。MP 模式通过块 ID 原生地访问 KV 存储(与 STORE / RETRIEVE 使用的单位相同),因此该端点完全以块为中心。旨在用于诊断和来自 lmcache bench server 的往返完整性检查——不用于推理数据路径。
请求主体:
字段 |
必需的 |
描述 |
|---|---|---|
|
是 |
引擎块 ID 作为整数的 JSON 列表,例如 |
|
是 |
正整数 — 每个哈希块的块数。 |
|
否(默认 |
引擎上注册的 KV 上下文 ID。 |
|
否(默认 |
如果为 |
响应 (200 OK):
{
"status": "success",
"chunk_size": 2,
"num_chunks": 2,
"chunk_checksums": ["<md5>", "<md5>"],
"layerwise": false,
"block_id_ranges": "0,[2,5],8"
}
当 layerwise=true 时,chunk_checksums 是一个以 "layer_<idx>" 为键的字典,其值是逐层列表。
HTTP 状态码:
200: 成功。400:block_ids为空,或者chunk_size缺失或非正数。404:instance_id未注册,或者注册的 KV 张量为空。501: 引擎没有cache_contexts,或者该端点不支持 KV 格式(页面缓冲融合和跨层布局在真正需要之前被拒绝)。503: 引擎尚未在app.state上初始化。
示例:
curl -s -X POST http://localhost:8080/cache/checksums \
-H 'Content-Type: application/json' \
-d '{"block_ids": [0, 1, 2, 3], "chunk_size": 2}'
缓存对象和预取#
两个端点 — DELETE /cache/objects 和 GET /cache/objects — 允许操作员从配置的缓存后端中清除键并列举当前驻留的内容。(要列举配置的后端本身,请在配置组中使用 GET /config/adapters。)
另一对 — POST /cache/prefetches 和 GET /cache/prefetches/{request_id} — 允许操作员(或协调者)**预热**节点的 L1 以便在流量到来之前从 L2 加载,并轮询加载直到完成;它们在本节末尾有文档说明。协调者提供了一个基于 instance_id 的变体 — 参见 多服务器协调。
两个对象端点都接受一个可选的 adapter 选择器 — 在 GET /cache/objects 上的查询参数(?adapter=<type_name>)和在 DELETE /cache/objects 上的主体字段。省略它将针对 **主要**(首次配置的)适配器。当多个适配器共享同一个 type_name 时,第一个匹配的适配器将生效。使用 GET /config/adapters 来了解有效的选择器。
这两个接口旨在用于操作员/管理员工作流程(“清除该用户的键”,“显示当前驻留的内容”,“在重命名后收集孤儿”)。它们**不**在推理数据路径上。
DELETE /cache/objects deletes from L1, L2, or both, selected by its tier
field (default l2). GET /cache/objects lists L2 only.
协调器的逐出循环自动使用 DELETE /cache/objects``(参见 :doc:`coordinator` — “L2 使用跟踪和逐出”);``GET /cache/objects 端点也支持协调器的启动重新同步。手动使用 curl 保留给临时操作员操作和调试。
DELETE /cache/objects#
Delete a caller-supplied list of keys from L1, L2, or both (tier).
Idempotent: absent keys are skipped silently. L2 deletes skip keys locked by
in-flight store/load tasks so an active transfer is never corrupted; L1 deletes
skip read/write-locked keys unless force is set. Blocking adapter I/O is run
off the event loop. When the tier includes L2 the primary (or selected) L2
adapter must be configured, else 503; a pure l1 delete needs no adapter.
Per-key successful L2 deletions fire on_l2_keys_deleted on the
adapter's listeners — when the coordinator is wired (see
--coordinator-l2-event-reporting), the deletions show up at the
coordinator's POST /quota/events as "type": "delete" events. The
coordinator's eviction + usage trackers learn about the deletion from
that event flow, not from the response of this call.
请求主体:
字段 |
类型 |
描述 |
|---|---|---|
|
列表[编码对象键] |
必填。要删除的对象键(见下方模式)。每个请求的批量限制为 |
|
字符串 |
Optional (default |
|
布尔值 |
Optional (default |
|
字符串 |
Optional (default: primary, first-configured adapter). The |
每个 EncodedObjectKey 是
{
"chunk_hash_hex": "abc123...",
"model_name": "meta-llama/Llama-3-8B",
"kv_rank": 0,
"object_group_id": 0,
"cache_salt": "user-a"
}
object_group_id``(默认 ``0)和 cache_salt``(默认 ``"")是为了与旧的 wire 负载向后兼容而可选的。
响应 (200 OK):
{
"deleted": 4,
"skipped": 0,
"ok": true
}
deleted is the total keys removed across the requested tiers (L1 removals
plus the L2 batch size); skipped is the L1 keys refused because they were
locked (non-force only). On an L2 adapter failure the response is still 200
with ok=false and an error field carrying the reason.
HTTP 状态码:
200: request processed (checkokfor the L2 adapter outcome).400: 批量超出限制,或者一个键负载违反了ObjectKey不变式(错误的十六进制,model_name中的@,禁止的cache_salt字符)。404: ``adapter``(主体)与任何已配置的适配器不匹配。422: Pydantic 级别的主体形状失败(缺少keys,字段类型错误)。503: engine not initialized, or no L2 adapters configured (only whentierincludes L2).
Example: delete from both tiers.
curl -s -X DELETE http://localhost:8080/cache/objects \
-H 'Content-Type: application/json' \
-d '{
"tier": "all",
"keys": [
{"chunk_hash_hex": "aa", "model_name": "m",
"kv_rank": 0, "object_group_id": 0, "cache_salt": "user-a"}
]
}'
# -> {"deleted": 2, "skipped": 0, "ok": true}
GET /cache/objects#
分页当前驻留在一个 L2 适配器中的键。
查询参数:
名称 |
默认 |
描述 |
|---|---|---|
|
主适配器 |
目标适配器的 |
|
无 |
将结果限制为与 |
|
|
每页最大条目数。必须在 |
|
无 |
来自上一页的 |
页面令牌是适配器私有的;请勿解析或修改它。支持列出功能的适配器(目前仅通过 ListObjectsV2 的 S3 适配器)保证尽力而为的一致性,而不是快照隔离——在分页遍历期间的并发存储或删除可能导致键出现、消失或在页面之间移动。
响应 (200 OK):
{
"adapter": "S3L2Adapter",
"entries": [
{
"key": {
"chunk_hash_hex": "abc123",
"model_name": "meta-llama/Llama-3-8B",
"kv_rank": 0,
"object_group_id": 0,
"cache_salt": "user-a"
},
"size_bytes": 4194304
}
],
"next_page_token": "opaque-cursor-string"
}
next_page_token 在列出内容耗尽时为 null。
HTTP 状态码:
200: 成功。400: 错误的page_token(适配器级别)。404:?adapter=<name>与任何已配置的适配器不匹配。422:page_size超出[1, 5000]范围。501: 选定的适配器不支持列出。在 v1 中,只有S3L2Adapter支持;由SerdeL2AdapterWrapper包装的适配器继承被包装适配器的行为。503: 引擎未初始化,或未配置 L2 适配器。
示例: 为模型分页每个键。
next=""
while :; do
page=$(curl -s "http://localhost:8080/cache/objects?model_name=meta-llama/Llama-3-8B&page_size=500&page_token=$next")
echo "$page" | jq '.entries[]'
next=$(echo "$page" | jq -r '.next_page_token // empty')
[ -z "$next" ] && break
done
POST /cache/prefetches#
通过从 L2 提前 加载一个令牌序列的块来预热一个节点的 L1,以便使用它们的第一个请求命中 L1,而不是在线支付 L2 获取的费用。当工作负载即将路由到一个节点(流量转移、热共享系统提示)时,这非常有用。
调用者通过 token ids 描述内容,而不是通过内部缓存键(键是内容哈希加上每个排名的布局位图,调用者无法构造)。服务器对 tokens 进行哈希,将每个块扩展到节点的各个排名,并提交一个 warm 预取:加载的块被 保留**(永久)并保持 **解锁 — 没有下游读取器来固定它们,因此后续的真实查找会自行加锁。调用立即返回;加载在存储管理器自己的线程中运行。它在所有配置的 L2 适配器之间合并,因此没有 ?adapter= 选择器。
请求主体:
字段 |
类型 |
描述 |
|---|---|---|
|
字符串 |
提供的模型 ID,完全按照注册的方式(例如 |
|
整数 |
节点的 KV 布局和排名分发中注册的值 vLLM(对于单 GPU,TP=1 部署为 |
|
列表[int] |
提示令牌 ID。必须使用与存储相同的分词器/特殊令牌设置,并且至少包含一个完整的 |
|
字符串 |
每个租户的密钥隔离;必须与存储匹配(默认 |
响应 (202 Accepted):
{"request_id": "abc123", "chunks": 12, "status": "submitted"}
当序列短于一个块时,不会提交任何内容,也没有 request_id 可供轮询:
{"chunks": 0, "status": "noop"}
HTTP 状态码:
202: 已提交(或如上所述的noop)。400:token_ids超过每个请求的上限,或cache_salt违反了其不变性。409: 未为(model_name, world_size)注册布局 — 该模型尚未在此节点上分配 KV Cache(请先启动 vLLM)。422: 请求体未通过字段级验证。503: 引擎未初始化。
示例:
curl -s -X POST http://localhost:8080/cache/prefetches \
-H 'Content-Type: application/json' \
-d '{"model_name": "Qwen/Qwen3-8B", "world_size": 1,
"token_ids": [101, 102, 103], "cache_salt": "user-a"}'
# -> {"request_id": "abc123", "chunks": 1, "status": "submitted"}
GET /cache/prefetches/{request_id}#
轮询已提交的热预取。热预取不持有锁,因此轮询仅报告进度;第一个观察到完成的轮询将删除作业(精确一次),因此对同一 ID 的后续轮询返回 404。
响应 (200 OK) 当负载运行时:
{"status": "pending"}
…并且一旦完成:
{"status": "completed", "found_keys": 12, "total_keys": 12}
found_keys / total_keys 计数仅包括由此请求 从 L2 加载的 块;已经驻留在 L1 的块在 reserve_write 时被跳过且不计入,因此部分驻留的热请求会低估驻留块的数量(冷请求会加载并计算所有内容)。热请求使用间隙容忍的 SPARSE 剪裁策略,因此一个已经驻留的块不会阻止其他块的加载——它会加载每个尚未驻留的块并报告该数量。不计算驻留块是故意的:一个已经存在的条目可能是来自另一个查找的瞬态临时条目,因此将其视为热请求可能会误导。
HTTP 状态码:
200: 状态报告(pending或completed)。404: 未知request_id— 已经完成并消费,或从未提交。503: 引擎未初始化。
示例:
curl -s http://localhost:8080/cache/prefetches/abc123
# -> {"status": "completed", "found_keys": 1, "total_keys": 1}
配额管理#
这些端点管理由 IsolatedLRU 逐出策略(通过 --eviction-policy IsolatedLRU 选择)消耗的每个 cache_salt 存储预算。配额是 软性 的:设置限制并不会拒绝写入 — 任何超出预算的 cache_salt 会在下一个逐出周期(约 1 秒)被逐出。没有注册配额的 cache_salt 有一个有效限制为 0 字节,因此其数据将在下一个周期被清除(白名单语义)。
对于未使用 --eviction-policy IsolatedLRU 启动的引擎,这些端点为空操作:QuotaManager 仍然存在,但 LRU 策略会忽略已注册的配额。
空盐的 URL 转义。 cache_salt=\"\"(无盐/匿名流量)不能出现在 URL 路径参数中,因此 API 接受哨兵 _default 作为替代。PUT /quota/_default 设置 cache_salt=\"\" 的配额,_default 在空盐的响应中被回显。合法存储数据的用户 cache_salt=\"_default\" 不能通过此 HTTP API 与匿名流量区分管理——两者映射到相同的路径参数;选择任何其他值(例如 \"default\")以消除歧义。
PUT /quota/{cache_salt}#
创建或更新配额。
路径参数: cache_salt — 租户标识符(使用 _default 表示空盐;请参阅章节介绍)。
请求主体:
字段 |
类型 |
描述 |
|---|---|---|
|
浮点数 |
必需。配额以 GB 为单位。必须是有限的且非负的。 |
响应 (200 OK):
{"cache_salt": "alice", "limit_gb": 10.0, "status": "ok"}
HTTP 状态码:
200: 配额已设置或更新。400: JSON 格式错误,缺少limit_gb,limit_gb非数字,nan/inf,或负值。503: 引擎未初始化。
示例:
curl -s -X PUT http://localhost:8080/quota/alice \
-H 'Content-Type: application/json' \
-d '{"limit_gb": 10.0}'
GET /quota/{cache_salt}#
读取当前配额和一个 cache_salt 的实时使用情况。
路径参数: cache_salt — 租户标识符(使用 _default 表示空盐)。
响应 (200 OK):
{
"cache_salt": "alice",
"limit_gb": 10.0,
"current_usage_gb": 2.137,
"exists": true
}
exists 为 false 当这个 cache_salt 从未注册过配额时(limit_gb 此时为 0.0,而 current_usage_gb 反映当前为该盐缓存的字节数——这些字节将在下一个周期下逐出 IsolatedLRU)。这个端点对于未知的盐永远不会返回 404。
HTTP 状态码:
200: 返回配额和使用情况(即使盐没有注册配额——此时exists为false;永远不会404)。503: 引擎未初始化。
示例:
curl -s http://localhost:8080/quota/alice | jq
DELETE /quota/{cache_salt}#
删除 cache_salt 的配额条目。任何仍然缓存于此 cache_salt 下的字节将在下一个逐出周期中超出预算(有效限制降至 0),并将被逐出。
路径参数: cache_salt — 租户标识符(使用 _default 表示空盐)。
响应 (200 OK):
{"cache_salt": "alice", "status": "removed"}
当给定的 cache_salt 没有注册配额时,响应为 {\"cache_salt\": \"...\", \"status\": \"not_found\"}(仍然是 200 OK)。
HTTP 状态码:
200: 配额条目已移除 ("removed") 或不存在 ("not_found");永远不会是404。503: 引擎未初始化。
示例:
curl -s -X DELETE http://localhost:8080/quota/alice
GET /quota#
列出每个注册的配额及其实时使用情况。
响应 (200 OK):
{
"users": {
"alice": {"limit_gb": 10.0, "current_usage_gb": 2.137},
"bob": {"limit_gb": 4.0, "current_usage_gb": 0.812}
}
}
只有带有 注册 配额的 cache_salt 值会出现;空盐会在 _default 键下报告。
HTTP 状态码:
200: 返回配额列表 ({\"users\": {}}当没有注册时)。503: 引擎未初始化。
示例:
curl -s http://localhost:8080/quota | jq
运行时 L2 重新配置#
这些端点在服务器具有可运行时重新配置的 L2 适配器时可用。它们仅更改 LMCache 的运行时映射和元数据;后端资源如 DAX 设备路径必须已经存在,并且服务器必须能够读取和写入。该端点将 backend、operation 和 JSON 请求体路由到通用 L2 适配器重新配置 API,而后端特定的验证和迁移语义则保留在适配器内部。
backend 和 operation 路径段被规范化(去除空格并转为小写)。在请求体内,adapter_index(默认为 0)是 后端本地 的——它仅索引该后端的适配器,而不是整个引擎的适配器列表。如果 L2 适配器被序列化/反序列化包装,后端字符串仍然是配置的 L2 适配器类型,而不是序列化/反序列化包装类型。
备注
这里接受的后端字符串通过 GET /config/adapters 发现:每个 reconfigurable 标志为 true 的适配器都可以通过其 type_name 进行访问。
GET /reconfigure/{backend}/status#
报告一个后端类型的运行时可管理适配器。每个适配器条目的 adapter_index 被重写为其 后端本地 的 0 基索引(在操作请求体中传回的值)。
路径参数: backend — 适配器 type_name``(标准化:去除空格并小写;通过 ``GET /config/adapters 发现有效值)。
响应 (200 OK):
{
"enabled": true,
"backend": "dax",
"num_adapters": 1,
"adapters": [
{"adapter_index": 0, "...": "backend-specific adapter fields"}
]
}
一个未知或空的后端返回 enabled=false,num_adapters=0,adapters=[](这不是 404)。
HTTP 状态码:
200:返回的状态(包括上述未知后端的情况)。400:backend为空。503: 引擎未初始化。
示例:
curl -s http://localhost:8080/reconfigure/dax/status | jq
POST /reconfigure/{backend}/{operation}#
将一个重新配置操作应用于后端适配器。请求体是一个 JSON 对象,其接受的字段取决于后端和操作。200 响应是存储管理器的 reconfigure_l2_adapter 返回的内容(一个后端定义的字典)。
路径参数: backend``(适配器 ``type_name)和 operation``(例如 ``add / remove / resize 用于 dax)。两者都经过标准化(去除空格并转为小写)。
对于 通用 路径(任何后端除了 dax),请求体包含 adapter_index 以及任何特定于后端的字段,这些字段会逐字转发给适配器。
对于 Device-DAX (backend=dax),使用 JSON 请求体,因为 DAX 路径包含斜杠。接受的操作和字段包括:
操作 |
主体字段 |
|---|---|
|
|
|
|
|
|
size 接受一个整数字节数或带有基于 1024 的单位后缀的字符串(b、kib、mib、gib、tib 及其 k/m/g/t 别名),例如 "100GiB";它必须解析为一个正值。
响应 (200 OK):
主体是后端的 reconfigure_l2_adapter 结果(一个后端定义的字典)。成功的 DAX add 看起来像这样:
{
"status": "ok",
"operation": "add",
"adapter_index": 0,
"device": {"device_path": "/dev/dax0.0", "state": "active", "size_bytes": 107374182400}
}
HTTP 状态码:
200: 成功(主体是存储管理器的重新配置结果)。400: 空的backend/operation,不支持的 DAX 操作,或无效的size。404:adapter_index超出后端范围。422: 请求体验证失败(例如,缺少必填字段或 DAX 请求体中的未知字段——DAX 请求体会拒绝额外字段)。503: 引擎未初始化。
示例:
curl -s -X POST http://localhost:8080/reconfigure/dax/add \
-H 'Content-Type: application/json' \
-d '{"device_path": "/dev/dax0.0", "size": "100GiB"}'
有关详细的请求示例、模式语义和验证指导,请参阅 设备-DAX (/dev/dax)。
可观察性#
GET /metrics#
默认 prometheus_client 注册表中注册的每个指标的 Prometheus 展示格式 (Content-Type: text/plain)。直接从 Prometheus 抓取此数据。有关导出指标的列表,请参见 可观察性。
响应 (200 OK, text/plain): Prometheus 展示格式的指标主体。
HTTP 状态码:
200:返回的指标。
示例:
curl -s http://localhost:8080/metrics
POST /metrics/reset#
将所有 LMCache 可观察性指标重置为初始状态(reset_observability_metrics)。面向测试框架和基准测试,不适用于生产环境。
响应(200 OK, text/plain):
ok
HTTP 状态码:
200:指标已重置。
示例:
curl -s -X POST http://localhost:8080/metrics/reset
诊断与调试#
GET /loglevel#
在运行时检查或修改 Python 日志记录器级别。所有响应都是 text/plain。该端点有三种模式,由查询参数驱动:
查询 |
行为 |
|---|---|
(无参数) |
列出所有在 |
|
返回指定记录器的有效级别。 |
|
将命名的日志记录器(及其处理程序)设置为 |
传递 level 而不带 logger_name 不匹配任何模式,并返回 200 和一个 null 响应体。
响应 (200 OK, text/plain): 每个注册的日志记录器一行 ``<logger>: <LEVEL>``(列表模式),一个 ``<logger>: <LEVEL>``(读取模式),或更新级别的确认(设置模式)。
HTTP 状态码:
200: 列出、读取或设置的级别(也包括上述的null-body 情况)。400:level不是已知的日志级别。
示例:
# list everything
curl -s http://localhost:8080/loglevel
# read one
curl -s 'http://localhost:8080/loglevel?logger_name=lmcache'
# elevate to DEBUG
curl -s 'http://localhost:8080/loglevel?logger_name=lmcache&level=DEBUG'
GET /threads#
列出服务器进程中活动的 Python 线程及其堆栈跟踪,并提供总计摘要(Content-Type: text/plain)。对于实时调试挂起或失控的工作线程非常有用。
查询 |
行为 |
|---|---|
|
仅保留名称包含 |
|
仅保留 |
警告
响应包含实时堆栈跟踪,并可能泄露内部代码路径和状态。在生产环境中限制对该端点的网络访问。
响应 (200 OK, text/plain):总线程数摘要,后面跟着每个线程的名称、ident 和当前堆栈跟踪。
HTTP 状态码:
200: 返回线程列表。
示例:
curl -s 'http://localhost:8080/threads?name=periodic'
GET /periodic-threads#
返回 PeriodicThreadRegistry 的 JSON 快照:按级别统计以及每个线程的状态(上次运行时间戳、最新摘要等)。
查询 |
行为 |
|---|---|
|
仅包含给定级别的线程。对未知情况返回 |
|
仅包含当前正在运行的线程。 |
|
仅包括被视为活动的线程(最近的滴答)。 |
响应 (200 OK):
{
"summary": {
"total_count": 4,
"running_count": 4,
"active_count": 4,
"by_level": {
"critical": {"total": 1, "running": 1, "active": 1},
"high": {"total": 2, "running": 2, "active": 2},
"medium": {"total": 1, "running": 1, "active": 1},
"low": {"total": 0, "running": 0, "active": 0}
}
},
"threads": [
{
"name": "...",
"level": "high",
"interval": 5.0,
"is_running": true,
"is_active": true,
"last_run_ago": 1.2,
"total_runs": 120,
"failed_runs": 0,
"success_rate": 100.0,
"last_summary": {"...": "..."}
}
]
}
HTTP 状态码:
200: 返回快照。400: 未知level过滤器。
示例:
curl -s 'http://localhost:8080/periodic-threads?level=critical' | jq
GET /periodic-threads/{thread_name}#
单个周期性线程的详细状态(与上面 threads 列表中显示的每个线程对象相同)。
路径参数: thread_name — 注册的周期线程名称。
响应 (200 OK):
{
"name": "storage-flush",
"level": "critical",
"interval": 5.0,
"is_running": true,
"is_active": true,
"last_run_ago": 1.2,
"total_runs": 120,
"failed_runs": 0,
"success_rate": 100.0,
"last_summary": {"...": "..."}
}
响应 (404 Not Found) 当名称未知时:
{"error": "Thread not found: <name>"}
HTTP 状态码:
200: 返回的线程状态。404: 没有名为该名称的周期性线程。
示例:
curl -s http://localhost:8080/periodic-threads/storage-flush | jq
GET /periodic-threads-health#
快速健康检查,仅涵盖 critical 和 high 级别的周期性线程。当线程被标记为运行但在预期间隔内未进行计时时,它会被标记为不健康。始终返回 200 — 健康状态通过 healthy 布尔值传达,而不是通过 HTTP 状态。
响应 (200 OK):
{
"healthy": true,
"unhealthy_count": 0,
"unhealthy_threads": []
}
当出现滞后时:
{
"healthy": false,
"unhealthy_count": 1,
"unhealthy_threads": [
{
"name": "storage-flush",
"level": "critical",
"last_run_ago": 42.5,
"interval": 5.0
}
]
}
HTTP 状态码:
200:健康报告 — 始终为200,即使在不健康时(healthy布尔值为false)。
示例:
curl -s http://localhost:8080/periodic-threads-health
GET /env#
将进程环境变量转储为排序后的美观打印 JSON 文档。响应的 Content-Type 为 text/plain,因此可以直接通过管道传输到终端。
警告
有效负载包含 每个 环境变量,包括通过环境注入的任何秘密。没有任何编辑或身份验证 — 在生产环境中限制对该端点的网络访问。
响应(200 OK, text/plain):
{
"HOME": "/root",
"LMCACHE_LOG_LEVEL": "INFO",
"PATH": "/usr/local/bin:/usr/bin"
}
HTTP 状态码:
200: 环境转储已返回。
示例:
curl -s http://localhost:8080/env
POST /run_script#
在服务器进程中执行上传的 Python 脚本。脚本作为多部分表单数据上传,字段名称为 script,并在受限的 __builtins__ 下被 exec 执行(仅允许 print、str、int、float、list、dict、tuple、set 和受保护的 __import__)。仅允许导入在 --script-allowed-imports 中列出的模块;运行的 FastAPI app 会注入到脚本的全局变量中。如果脚本赋值给名为 result 的变量,则返回其字符串化值;否则返回正文为 "脚本成功执行"(Content-Type: text/plain)。
请求体: 多部分表单数据,包含一个 script 文件字段(要执行的 Python 源代码)。不是 JSON 格式的请求体。
危险
此端点在进程中运行调用者提供的代码。受限的内置函数不是安全沙箱——结合注入的app对象和任何允许的导入,将其视为完全的远程代码执行。切勿在不受信任的网络上公开它。
响应 (200 OK, text/plain): 如果脚本分配了字符串化的 result 变量,则返回该变量,否则返回 脚本成功执行。
HTTP 状态码:
200: 脚本已执行。400: 未提供script文件。500: 在导入设置或执行期间引发了异常(主体:"执行脚本时出错:<reason>")。
示例:
curl -s -X POST http://localhost:8080/run_script \
-F 'script=@my_script.py'
添加新端点#
端点会从 lmcache/v1/multiprocess/http_apis/ 自动发现。要添加一个新的仅限 MP 的端点:
在该目录中创建一个名为
<name>_api.py的新模块。定义一个模块级的
router = APIRouter()。使用 FastAPI 装饰器在
router上注册处理程序。通过
request.app.state.engine访问引擎,并检查None情况(引擎尚未初始化)。
HTTPAPIRegistry 将在启动时自动加载模块 — 无需编辑中央注册列表。
如果路由足够通用,可以与嵌入 vLLM 的 API 服务器共享,请将其添加到 lmcache/v1/internal_api_server/common/ 下。它将在 MP 端通过 common_api.py 被加载,除非其模块名称在 _MP_INCOMPATIBLE_MODULES 中列出(该列表保留给需要 vLLM 特定 app.state 属性的模块;当前列表为空)。位于 internal_api_server/vllm/ 下的处理程序仍然可以通过从 http_apis/ 下的组模块中包含其路由器在 MP 服务器上显示(如 info_api.py 对版本端点所做的那样)。
添加新端点时,请在此页面上添加一个对应的章节,说明该端点的用途、请求/响应结构以及一个示例 curl 调用。