LMCache MP 模式运行时插件(Runtime Plugin)开发指南:基于 ZMQ 多进程服务器扩展自定义脚本 📅 发布时间:2026/9/16 17:05:19 👁 浏览次数: LMCache MP 模式运行时插件Runtime Plugin开发指南基于 ZMQ 多进程服务器扩展自定义脚本【免费下载链接】LMCacheLMCache: Supercharge Your LLM with the Fastest KV Cache Layer项目地址: https://gitcode.com/GitHub_Trending/lm/LMCache导读LMCache 的 multiprocessMP / ZMQ服务器模式允许用户将自定义的 Python 或 Bash 脚本作为运行时插件与服务器同生命周期运行用于状态上报、心跳保活、前端服务托管、文件上传等运维与扩展场景。本文以仓库 examples/mp_runtime_plugins/README.md 为主体结合 MP Runtime Plugin 设计文档 与核心源码完整讲解 MP 模式插件的设计原理、聚合配置 JSON 结构、启动方式、插件契约与两个参考实现并给出可复制的实战案例。读完本文你将能够编写符合契约的 MP 运行时插件、通过--runtime-plugin-locations一键加载、理解LMCACHE_RUNTIME_PLUGIN_CONFIG环境变量的完整内容与解析方式以及复用前端托管、心跳上报等真实场景的插件范式。一、背景为什么 MP 模式需要单独的插件机制LMCache 同时存在两种运行模式非 MP 模式vLLM 集成插件通过RuntimePluginLauncher启动环境变量中携带的是单个LMCacheEngineConfig的 JSON并且服务器内部存在SCHEDULER/WORKER等多角色划分插件文件名前缀用于角色过滤。MP 模式multiprocess / ZMQ 服务器配置由多个独立 dataclass 构成包括MPServerConfig、StorageManagerConfig、ObservabilityConfig、HTTPFrontendConfig等。因此插件接收到的不是单一配置而是聚合后的 JSON dict包含mp_config、storage_manager_config、obs_config等分节见 mp_runtime_plugin_launcher.py 模块级注释。MP 模式的另一关键差异是没有角色role概念整个 MP 服务器运行在单个进程内不存在 TP 式 worker 分片。因此基于文件名的SCHEDULER/WORKER前缀过滤在 MP 模式下完全失效插件会全部启动。这一点在 examples/mp_runtime_plugins/README.md 中明确说明。二、架构与核心组件MP 运行时插件框架的整体架构如下图所示类图源自 设计文档MPRuntimePluginLaunchermp_runtime_plugin_launcher.pyMP 模式专属入口。通过**kwargs接收任意 dataclass 配置调用safe_asdict()将其序列化为单个 JSON dict构造_MPPluginConfig包装器后将全部进程管理委托给基础RuntimePluginLauncher。RuntimePluginLauncherruntime_plugin_launcher.py基础启动器负责插件发现.py/.sh、角色与 worker ID 过滤roleNone时跳过、解释器探测shebang → 回退、subprocess.Popen子进程管理、stdout 实时捕获与atexit优雅退出。RuntimePluginConfigconfig.py插件配置 dataclass含locations插件文件/目录路径列表与extra_config透传给插件的额外键值对命令行以 JSON 字符串接受。_MPPluginConfigmp_runtime_plugin_launcher.py薄包装 dataclass满足RuntimePluginLauncher对 config 的鸭子类型契约runtime_plugin_locationsto_json()。to_json()将聚合配置与extra_config合并后输出 JSON 字符串。safe_asdict/make_json_safelmcache.v1.utils.json_utils公共工具将 dataclass 转为 dict 并处理不可序列化字段如pathlib.Path回退为str()make_json_safe递归清洗任意值也被/configHTTP 端点复用。关键设计决策MPRuntimePluginLauncher.__init__中mp_runtime_plugin_launcher.py硬编码了三个 MP 模式专属参数参数MP 模式取值含义roleNone关闭基础启动器的文件名前缀角色过滤worker_count1MP 服务器只有单个进程实例worker_id0恒为 worker 0从源码结构看这一约定使 MP 模式插件无需遵循ROLE_WORKERID_NAME命名规范如server_0_foo.py任何.py/.sh文件都会被直接加载。三、数据流从服务器启动到插件日志回灌设计文档 Data Flow 给出了完整时序MP 服务器初始化MPRuntimePluginLauncher(runtime_plugin_config, mp_config, storage_manager_config, obs_config, ...)启动器对每个配置调用safe_asdict()构建_MPPluginConfig包装器以roleNone构造内部RuntimePluginLauncherlaunch_plugins()递归发现.py/.sh文件跳过角色/worker 过滤通过subprocess.Popen启动插件子进程并设置PYTHONUNBUFFERED1与聚合配置环境变量插件解析LMCACHE_RUNTIME_PLUGIN_CONFIG后向 stdout 输出启动器后台线程逐行读取 stdout通过logger.info([%s] %s, plugin_name, line)回灌到 LMCache 日志runtime_plugin_launcher.py服务器关闭时stop_plugins()对每个存活进程调用proc.terminate()。在 http_server.py 中可以验证实际的接线逻辑当mp_config.runtime_plugin_config.locations非空时构造MPRuntimePluginLauncher并launch_plugins()且http_config会被作为额外配置节透传给插件——这正是前端插件能自动推导 HTTP 端口的原因。四、环境变量契约基础启动器为每个插件子进程设置以下环境变量runtime_plugin_launcher.py变量MP 模式取值描述LMCACHE_RUNTIME_PLUGIN_CONFIG聚合 JSON 字符串完整服务器配置核心变量LMCACHE_RUNTIME_PLUGIN_ROLE空MP 模式无角色LMCACHE_RUNTIME_PLUGIN_WORKER_COUNT1单进程LMCACHE_RUNTIME_PLUGIN_WORKER_ID0恒为 0PYTHONUNBUFFERED1强制实时 stdout避免阻塞缓冲导致日志延迟同时设置LMCACHE_PLUGIN_*旧版别名以保持向后兼容源码中有# TODO: For backwards compatibility, remove when applicable注释提示这些旧变量将来可能移除。五、聚合配置 JSON 结构MP 模式MP 模式下LMCACHE_RUNTIME_PLUGIN_CONFIG的值是如下形态的 JSON dict取自 examples/mp_runtime_plugins/README.md更完整版本见 设计文档{ mp_config: { host: localhost, port: 5555, chunk_size: 256, max_workers: 1, hash_algorithm: blake3, engine_type: default, runtime_plugin_locations: [examples/mp_runtime_plugins/] }, storage_manager_config: { l1_manager_config: { memory_config: { size_in_bytes: 10737418240, use_lazy: true } }, eviction_config: { eviction_policy: LRU, trigger_watermark: 0.8, eviction_ratio: 0.2 } }, obs_config: { enabled: true, metrics_enabled: true, logging_enabled: true, tracing_enabled: false } }各分节与服务器配置 dataclass 一一对应字段含义可从 config.py 中的MPServerConfighost默认localhost、port默认5555、chunk_size默认256、max_workers默认1、hash_algorithm默认blake3、engine_type默认default见 config.py得到印证mp_configZMQ 服务器配置。engine_type支持default/blend等取值其中blend模式额外受后续 CacheBlend 专用字段影响。storage_manager_config存储管理配置。l1_manager_config.memory_config.size_in_bytes即 L1 缓存内存字节数10GBuse_lazy表示是否启用惰性分配eviction_config控制缓存淘汰策略LRU、水位触发阈值trigger_watermark0.8与单次淘汰比例eviction_ratio0.2。obs_config可观测性配置分别控制全局开关、metrics、logging 与 tracing。此外当RuntimePluginConfig.extra_config非空时_MPPluginConfig.to_json()会将其以runtime_plugin_extra_config键并入顶层mp_runtime_plugin_launcher.py当通过http_server.py启动且启用了 HTTP 前端时还会额外携带http_config节含http_host/http_port/http_socket_path。六、快速开始加载示例插件启动命令在仓库根目录执行以下命令即可让 MP 服务器加载examples/mp_runtime_plugins/下的全部插件examples/mp_runtime_plugins/README.mdpython -m lmcache.v1.multiprocess.server \ --host localhost --port 5555 \ --l1-size-gb 10 \ --eviction-policy LRU \ --runtime-plugin-locations examples/mp_runtime_plugins/对应命令行参数在 config.py 的add_mp_server_args中解析--runtime-plugin-locations对应RuntimePluginConfig.locations--runtime-plugin-configJSON 字符串对应extra_configconfig.py 显示了两者的组装逻辑。预期日志输出插件启动后服务器日志中会出现类似输出[mp_plugin] Started [mp_plugin] MP server: hostlocalhost port5555 chunk_size256 [mp_plugin] Storage: L110.0GB evictionLRU watermark0.8 [mp_plugin] heartbeat #0注意实际日志中每行会被包装为logger.info([插件文件名] 内容)的形式例如[mp_plugin.py] Started因为捕获线程按行记录见 runtime_plugin_launcher.py。七、参考插件实现深度解析7.1 Python 插件mp_plugin.py完整源码见 examples/mp_runtime_plugins/mp_plugin.py它示范了 MP 插件的三个核心技能点① 优雅退出SIGTERM / SIGINTdef handle_exit(signum, frame): Graceful exit on SIGTERM / SIGINT. print([mp_plugin] Received termination signal, exiting...) sys.exit(0) signal.signal(signal.SIGTERM, handle_exit) signal.signal(signal.SIGINT, handle_exit)当服务器关闭调用proc.terminate()时插件会收到 SIGTERM 并干净退出。② 解析聚合配置raw os.getenv(LMCACHE_RUNTIME_PLUGIN_CONFIG, ) if not raw: print([mp_plugin] WARNING: LMCACHE_RUNTIME_PLUGIN_CONFIG is empty) return {} try: return json.loads(raw) except json.JSONDecodeError as exc: print([mp_plugin] ERROR: failed to parse config: %s % exc) return {}对缺省与解析失败都做了兜底返回空 dict 而非抛异常——这是健壮插件的推荐写法。③ 单行输出 周期心跳def dump_parsed_config(config: dict) - None: print([mp_plugin] config: %s % json.dumps(config, defaultstr)) while True: print([mp_plugin] heartbeat #%d % loop_count) loop_count 1 time.sleep(30)源码注释明确说明父进程逐行捕获 stdout 并分别记日志因此插件应输出单行消息且用json.dumps(..., defaultstr)将配置压缩为单行 JSON保证一条配置只占一条日志。7.2 Bash 插件mp_heartbeat.sh完整源码见 examples/mp_runtime_plugins/mp_heartbeat.sh展示了 Bash 侧的等价实现通过trap echo ...exiting...; exit 0 SIGTERM SIGINT实现优雅退出从环境变量读取LMCACHE_RUNTIME_PLUGIN_CONFIG可选依赖jq若存在jq则用jq -c .输出压缩单行 JSON否则回退为原始字符串command -v jq /dev/null ... || ...分支同样以 30 秒间隔运行心跳循环。该脚本展示了尽量零依赖的设计思路jq仅作增强非必需。启动器会先读取文件首行 shebang#!/bin/bash探测解释器失败时按扩展名回退到bash见 runtime_plugin_launcher.py。八、插件契约Plugin Contract设计文档 Plugin Contract 明确了插件必须/应该满足的要求必须是runtime_plugin_locations配置目录下的.py或.sh文件能被探测到的解释器Python 或 Bash执行按需从LMCACHE_RUNTIME_PLUGIN_CONFIG环境变量读取配置。应该向 stdout 打印状态/心跳消息会被启动器捕获并以logger.info记录优雅处理SIGTERM以实现干净关闭输出单行消息每行 stdout 对应一条独立日志。九、插件发现与过滤机制RuntimePluginLauncher._launch_plugins的发现流程runtime_plugin_launcher.py若loc是文件直接使用该文件若是目录则用Path.rglob(*.py)Path.rglob(*.sh)递归收集全部插件对每个文件调用_should_skip_plugin做角色/worker 过滤——role is NoneMP 模式时直接跳过全部过滤runtime_plugin_launcher.py通过_get_interpreter解析 shebang 探测解释器shutil.which逐个尝试找不到则抛ValueErrorsubprocess.Popen([interpreter, str(file)], env..., stdoutPIPE, stderrSTDOUT)启动子进程启动守护线程_capture_plugin_output实时读取并记录输出。若配置的 location 不存在启动器仅记录 warning 后继续logger.warning(Runtime plugin location %s does not exist, loc)不会中断服务器。十、实战用例用例 1HTTP 前端插件服务发现心跳场景在 LMCache MP 服务器旁运行一个 HTTP 前端进程定期向发现服务发送心跳使集中式前端能够跟踪存活的 LMCache 节点。组件插件脚本 lmcache_mp_frontend_plugin.py读取聚合配置并启动前端应用发现服务 simple_discover_service.py接收心跳并暴露节点列表前端应用 app.py集中式 LMCache 前端代理请求到后端节点。启动命令设计文档# 1. 启动发现服务 python -m lmcache.tools.simple_discover_service # 2. 启动带前端插件的 LMCache MP 服务器 python -m lmcache.v1.multiprocess.http_server \ --host localhost --port 5555 \ --l1-size-gb 10 \ --http-host 0.0.0.0 --http-port 8080 \ --runtime-plugin-locations lmcache/lmcache_frontend/lmcache_mp_plugin/lmcache_mp_frontend_plugin.py \ --runtime-plugin-config {plugin.frontend.heartbeat_url: http://localhost:5000/heartbeat}插件如何利用配置lmcache_mp_frontend_plugin.py 的build_argv纯函数从http_config读取http_host/http_port自动构造--nodes参数0.0.0.0是监听地址而非可连接地址会被改写为localhost从runtime_plugin_extra_config读取所有plugin.frontend.*键转换为app.main()的 CLI 参数如--heartbeat-url追加--port用于心跳构建api_address与--no-http。build_argv被设计为纯函数以便单元测试且兼容旧键名http_frontend_config。该插件还示范了开发环境的 import 回退当lmcache未安装时自动将仓库根目录加入sys.path后重试导入lmcache_mp_frontend_plugin.py。配套一键启动脚本见 run_mp_server_with_frontend.sh。用例 2Chunk Hash 文件上传 Agent场景LMCache 运行期间会产出 JSONL 格式的 chunk hash 文件插件可以作为 Agent 周期性扫描并将这些文件上传到远程存储S3、HDFS、OSS 等。插件职责设计文档从LMCACHE_RUNTIME_PLUGIN_CONFIG读取storage_manager_config确定 chunk hash 文件输出目录从runtime_plugin_extra_config读取上传目标设置如plugin.uploader.s3_bucket、plugin.uploader.prefix周期性扫描输出目录并上传新产生的.jsonl文件上传成功后可选归档或删除本地文件。十一、常见注意事项输出必须是单行父进程按行捕获 stdout多行输出会被拆成多条日志破坏日志的可读性与可解析性需要结构化输出时请用json.dumps压缩为一行。插件目录会被递归扫描runtime_plugin_locations指向目录时rglob会递归收集所有.py/.sh注意避免把无关脚本放进插件目录。extra_config是插件间共享参数的自然通道通过--runtime-plugin-config {plugin.frontend.heartbeat_url: ...}传入的键值会以runtime_plugin_extra_config节出现在每个插件的配置 JSON 中且只有在非空时才会被合并_MPPluginConfig.to_json()中的条件判断。优雅退出务必处理 SIGTERM服务器关闭依赖proc.terminate()插件若忽略 SIGTERM 可能导致进程残留。MP 模式无角色过滤无需也不应依赖文件名前缀来区分插件所有插件都会启动这一点与 vLLM 集成模式的ROLE_WORKERID_NAME命名规范完全不同。十二、验证与测试MPRuntimePluginLauncher的行为有单元测试覆盖tests/v1/multiprocess/test_mp_runtime_plugin_launcher.py。测试通过 mockRuntimePluginLauncher验证聚合配置正确传入包装器、runtime_plugin_locations透传、extra_config合并等关键行为例如断言wrapper.runtime_plugin_locations [/plugins]。在修改或扩展插件框架时可运行该测试文件进行回归验证python -m pytest tests/v1/multiprocess/test_mp_runtime_plugin_launcher.py结语MP 运行时插件是 LMCache 多进程服务器对外扩展能力的关键接口它以环境变量传配置、子进程跑脚本、stdout 回灌日志的极简契约让运维监控、服务发现、数据上传等周边能力无需侵入服务器核心即可随服务器同生共死。开发者只需掌握聚合 JSON 的解析方式与单行输出、SIGTERM 处理两条纪律即可快速产出高可用插件——本文的两个参考实现与两个实战用例即为可直接套用的模板。【免费下载链接】LMCacheLMCache: Supercharge Your LLM with the Fastest KV Cache Layer项目地址: https://gitcode.com/GitHub_Trending/lm/LMCache创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考