3.5 KiB
3.5 KiB
心跳与重试机制设计 (qiming-mcp-stdio-proxy)
1. 背景与问题
目前 qiming-mcp-stdio-proxy 缺乏针对其传输层(特别是 sse 和 streamable 远程连接)的健壮的心跳(Keep-Alive)和透明重试机制。如果后端的 MCP 服务器崩溃或网络断开,依赖代理的下游 Agent 会遇到 stdio 管道断裂或请求挂起的问题。
我们需要一个具有弹性的代理层:
- 主动监控上游连接的健康状况(使用 Ping 等请求)。
- 如果连接断开,透明地重连并重新映射传输层。
- 对下游的 Agent(仅通过标准 stdio 通信)屏蔽这些临时故障,有效创建一个“永远在线”的代理抽象。
2. 核心功能与要求
2.1 心跳 (Keep-Alive)
- 间隔: 默认 20 秒 (
--ping-interval)。 - 超时: 默认 5 秒 (
--ping-timeout)。 - 方式: 发送 MCP 的 JSON-RPC
ping请求进行健康检查。 - 失败阈值: 连续 3 次健康检查失败。
2.2 重连与重试逻辑
- 当连接被认定为不健康(连续 3 次失败或传输层触发
onclose/onerror)时:- 状态变更为
RECONNECTING(重连中)。在重连期间到达的 MCP 请求会进入内部队列进行缓冲,代理本身绝不能崩溃。 - 代理启动对上游服务的重连(重新 spawn 标准子进程,或重新建立 SSE/Streamable HTTP 连接)。
- 重连成功后,代理将把内部的
Transport重新映射到新的连接,并自动刷出之前排队的请求。 - 下游 Agent 与代理之间的
stdio连接在整个过程中保持完好无损。
- 状态变更为
3. 架构设计
3.1 弹性代理传输层 (ResilientTransportWrapper)
我们使用 ResilientTransportWrapper 将底层的连接进行封装。这个 Wrapper 向上层环境暴露标准稳定的 Transport 接口,但内部动态管理和更替实际的 SSE/Streamable/Stdio 传输实例。
class ResilientTransportWrapper implements Transport {
// 实现 MCP Transport 接口
// 内部管理实际的连接 (activeTransport)
// 包含断开重连逻辑、心跳检测机制和消息排队发送队列
}
3.2 内部实现细节
- 连接建立: 初始化时通过外部传入的
connectParams工厂函数创建底层的 Transport 连接。 - 心跳定时器: 每隔固定时间(例如 20 秒),向
activeTransport发送原生的 JSON-RPCping请求。如果在发送期间抛错或传输直接断开,则累加连续失败计数。 - 断开拦截: 拦截
activeTransport的onclose和onerror事件,并触发内部重连,防止其向上冒泡直接中止下游的 stdio 代理进程。 - 请求排队: 当正处于重连状态时,由于暂时没有可用的底层发送通道,通过外层
send(message)收到的任何请求都会进入一个有容量限制(默认 100)的队列中。新的底层连接建立成功后,会迅速重放(flush)发送队列的所有请求。 - 多模式支持: 这个逻辑被集中继承到了
modes/convert.ts(独立服务转换模式) 及bridge.ts(PersistentMcpBridge持久化管理桥接) 内部,以实现对不同形式请求的通用重连保护。
4. 命令行及参数配置
新增了控制心跳频次的参数选项支持,可以通过启动命令或在配置文件内声明:
--ping-interval <毫秒>: 发送心跳 Ping 的时间间隔(如果不指定,默认为 20000 即 20 秒)。--ping-timeout <毫秒>: 心跳请求超时判断(如果不指定,默认为 5000 即 5 秒)。