MCP性能调优实战:如何将请求延迟从1200ms压到250ms MCPModel Context Protocol这个词最近在 AI 工程圈子里已经快被说烂了。但大多数讨论都停在怎么接入怎么封装真正到了生产环境你才会碰到那个让所有人头疼的问题延迟。尤其当你做的是 Agent 类应用、实时决策类服务一次工具调用的往返时间直接决定了用户是觉得这 AI 挺聪明还是这 AI 是不是卡了。我最近几个月一直在折腾 MCP 协议的底层优化把一次典型的 MCP 请求从平均 1200ms 压到了 250ms 以内整个过程踩了不少坑也总结出一些比较通用的方法论。这篇不是入门教程更偏实战复盘适合已经在用 MCP、正在被性能问题折磨的人。先说结论MCP 性能调优的本质不是把某个环节调快而是把整个请求链路里的等待全部找出来逐一消灭。毫秒级博弈这个词说的就是这种从传输层、序列化层、连接管理、服务端处理逻辑到客户端调度策略的全链路拉锯。1. 先把 MCP 的延迟构成拆明白从一次完整请求看看时间都去哪儿了1.1 一次典型 MCP 调用的链路拆解MCP 协议本质上是一个基于 JSON-RPC 2.0 的远程调用协议它的核心范式是客户端比如 Agent 的主进程通过某种传输方式向服务端比如一个工具服务、一个知识库服务发起请求。我们以最常用的 Streamable HTTP 传输模式为例拆解一次完整的工具调用流程客户端发起 HTTP POST 请求把 JSON-RPC 消息序列化成文本经过网络传输到达服务端服务端反序列化 JSON解析方法名和参数服务端执行实际业务逻辑可能涉及内部调用、查询、计算服务端把结果序列化为 JSON-RPC 响应响应经过网络返回客户端客户端反序列化响应这七步看似简单但每一步里都藏着延迟黑洞。我之前在一个模拟项目X里抓过一份全链路火焰图发现一个很有意思的现象真正花在业务逻辑上的 CPU 时间只占 18%剩下的时间几乎全消耗在连接建立、HTTP 头解析、JSON 序列化/反序列化、网络 RTT往返时延和线程调度上。这就引出一个核心认知你觉得自己在优化 MCP其实很多时候是在优化一个分布式系统的通用顽疾——传输与解析开销。如果你的服务端逻辑只需要 20ms但整体耗时 1200ms那问题一定不在逻辑本身而在那 1180ms 的路径消耗上。逐个环节分析连接层。如果每次请求都重新走一遍 TCP 三次握手 TLS 握手 HTTP 升级协议一次握手的开销在高延迟网络下可能达到 50-200ms。这是很多初版实现最容易忽略的问题——服务端默认的 HttpsServer 实现每个请求进来都可能触发新的连接协商。消息体处理层。MCP 使用 JSON-RPC其中 JSON 的序列化和反序列化在消息体较大时比如携带了 context 内容、工具返回的长文本消耗非常可观。我在实测中发现一个 50KB 的响应体如果服务端用的是标准库的 JSON 解析光序列化就能吃掉 15-40ms换个高效库这个数字能直接掉到 2ms 以下。业务逻辑层。服务端内部如果存在同步阻塞的数据库查询、外部 API 调用或者没有使用异步 IO这部分的耗时会被无限放大。这里有个很常见的反模式服务端在处理 MCP 请求时把notifications/cancelled或者别的消息处理放在同一个线程池里导致高负载下互相排队。1.2 延迟分水岭为什么毫秒级这么重要很多刚接触 MCP 的同学会问我的工具调用 500ms 也够用啊用户感知不明显为什么非要压到毫秒级这个问题的答案藏在 AI 应用的整体交互链路里。在典型的 Agent 应用中一次用户问题往往需要多次工具调用比如先查天气、再查日历、再查交通而且这些调用往往存在串行依赖。假设某个任务需要串行调用 5 次工具单次 500ms 就是 2.5 秒的额外等待单次压到 200ms总等待是 1 秒如果能到 80ms用户几乎无感知。更关键的是很多 Agent 框架比如常见的编排引擎会对工具的响应做多轮推理主模型的思考时间和工具调用时间往往是叠加关系。工具调用越慢主模型的等待就越久这会大幅拉长整体的首 token 时间和完成时间。从体验角度说300ms 是人类感知实时的临界点超过这个数值用户会明显感觉卡了一下。AI 助手和传统软件不太一样用户对传统软件的延迟容忍度高是因为预期操作后要处理数据但对 AI 期望的是像人一样快速思考并回应。所以毫秒级不是炫技是体验的基本盘。2. 传输选型与连接生命周期从 HTTP 短连接到长连接和连接池2.1 Streamable HTTP 模式下的连接复用策略MCP 规范提供了多种传输方式stdio、Streamable HTTP、HTTPWebSocket 等。很多人在开发期图省事直接用 stdio但到了生产环境尤其是多客户端并发场景HTTP 模式几乎是唯一选择。可一旦切到 HTTP 模式连接管理就成了第一个性能关卡。我在模拟项目X的早期版本里用的是 Python 的 FastAPI 加简单的/mcp端点每个请求进来都是独占连接。压力测试一跑50 并发就把请求队列打爆了。后来做了三件事效果立竿见影第一开启 keep-alive。确保 HTTP 客户端和服务端都支持长连接。对 HTTP/1.1 来说就是设置Connection: keep-alive并合理设置keep-alive超时时间我习惯设 30 秒。这样后续请求复用已建立的 TCPTLS 连接省去了每次握手的开销。实测下来一个首次请求 80ms 的调用含握手复用连接后可以降到 20ms 左右。第二引入连接池。客户端如果用的是常见的 SDK比如 Python 的httpx或者 Node 的undici默认就是有连接池的但要注意连接池的上限。我遇到过很尴尬的情况客户端连接池默认最大 10 条连接结果在 20 并发场景下有 10 个请求在排队等连接白白增加了阻塞延迟。把连接池上限调到 50-100同时设置合理的空闲回收策略能明显改善。第三注意客户端 DNS 缓存和 TLS 会话复用。这两个是细节但影响同样不小。DNS 解析每次 20-50ms如果客户端 SDK 缓存了 DNS比如httpx默认缓存 30 秒那在高频调用下基本可忽略但如果用的是某些不缓存的 HTTP 客户端每次请求都做一次 DNS 解析那就是灾难。TLS 方面服务端开启 TLS 会话缓存Session Resumption可以跳过完整的 TLS 握手只做一次轻量的恢复协商这一步能省掉 30-80ms。2.2 服务端连接配置的隐藏陷阱服务端的连接处理同样有讲究。传统的阻塞式 IO一个请求一个线程在 MCP 场景下非常不划算因为 MCP 的请求往往是短小精悍的 JSON-RPC线程切换和上下文切换的开销占比很高。随手写一个 FastAPI内部用 asyncio 事件循环都能比传统的 Gunicorn 同步 worker 好很多因为异步 IO 不会被 IO 等待阻塞。但如果服务端用了异步框架反而有另一个坑同步阻塞调用的混用。服务端在处理 MCP 请求时如果混入了同步阻塞的数据库操作或者调用了同步的第三方 SDK那这个异步事件循环就会被卡住所有并发请求全部排队。我在实战中处理过一个典型问题MCP 工具内部调用了一个同步的 Redis 客户端每次查询 50ms本来以为异步框架能扛 1000 并发结果实际峰值只有 20 并发。后来把所有同步调用换成异步版本并发能力直接上升了一个数量级。关于连接参数我整理了一个自己用的推荐起点参数项推荐起点值备注HTTP keep-alive 超时30-60s太短会导致频繁建连太长会占用资源客户端连接池最大连接数50-100根据并发上限调整宁多勿少TLS 会话缓存开启服务端配置ssl.SSLContext打开 session cacheHTTP/2 支持视情况开启h2 支持多路复用高并发下收益明显服务端最大并发连接默认不设限但需要配合任务队列做背压控制空闲连接回收5 分钟防止老连接占用路由器和中间设备的资源值得一提的是如果你的服务端能直接上 HTTP/2现在很多网关和服务端框架都支持多路复用能力会把客户端连接池的需求降低不少。因为 HTTP/2 可以在一条 TCP 连接上并发多个请求20 并发不再需要 20 条连接一条连接就够还避免了 TCP 队头阻塞。3. 序列化与消息体优化JSON-RPC 在毫秒级压力下的隐藏成本3.1 JSON 序列化库的实测对比MCP 的 JSON-RPC 消息体说大不大说小不小。一个工具调用的请求可能只有几百字节但响应可能很大——尤其是工具返回了长文本、文件片段或结构化数据时。在序列化这块我之前踩过很大的坑。灾难现场服务端用 Python 的json标准库客户端用requestsjson.loads()。单次调用 500 并发压测服务端 CPU 直接 100%平均延迟飙到 3 秒以上。后来做性能剖析发现服务端 CPU 时间分布大概是JSON 序列化/反序列化 45%业务逻辑 30%框架开销 25%。这就是典型的序列化瓶颈。MCP 场景下的消息其实是短小而频繁的字符串处理任务标准库 JSON 的解析和生成效率并不高。我做了个小实验用 Python 标准库json、orjson、ujson三个库分别对一段 30KB 的 MCP 响应做序列化结果序列化库序列化耗时30KB payload反序列化耗时json标准库约 1.5ms约 2.1msujson约 0.7ms约 1.2msorjson约 0.2ms约 0.4msorjson 在序列化方面比标准库快了 7 倍以上反序列化快了 5 倍以上。这还只是单次调用的差异在高并发下这个差距是叠加的CPU 占用率直接能降下来三分之一。如果你用的是 Node.js 那边其实不需要折腾 lib 替换因为 V8 底层的 JSON 解析已经很快了但如果你用的是 Go还得注意结构体的字段 tag 编码规范避免反射带来的额外损耗。但序列化库替换有个注意点必须确认它对 JSON-RPC 消息传递的所有类型都支持。MCP 协议里会用到null、整数、浮点数、字符串、布尔值、数组和对象orjson 对null和超大整数的支持都要小心验证别因为换了库导致边界数据丢失。3.2 消息结构瘦身的实操技巧序列化库只是第一层消息结构本身的体积也很重要。MCP 的请求和响应都遵循 JSON-RPC但内容字段是可以做文章的。我从三个方向做了消息体瘦身精简参数。有些客户端 SDK 在构造请求时会带上大量冗余的上下文信息比如全量的 system prompt、多轮对话记录这些如果都放进 parameters 里消息体很容易变成几十 KB。我做的优化是只传与当前工具调用相关的参数切片把上下文移到服务端缓存或单独的 context 管理通道。这一步直接把平均 payload 从 18KB 降到了 3KB。响应压缩。对于大响应超过 1KB 的文本开启 gzip 压缩。HTTP 传输层如果支持Content-Encoding: gzip压缩率在 JSON 上通常能达到 70%-85%。我实测一个 45KB 的响应开启 gzip 后传输体积只有 6KB网络传输时间从 30ms 降到 5ms按 10Mbps 带宽估算。注意压缩需要服务端 CPU 成本但在这个尺寸级别收益远大于成本。剔除无用字段。检查服务端返回的 JSON 里有没有对客户端没用的字段比如调试日志、时间戳、完整原始查询语句全部剔除。这需要客户端和服务端对齐接口契约把_meta之类的扩展字段约定清楚。这里有个非常容易被忽略的细节数字的精度。JSON-RPC 对整数的范围有约定如果服务端返回一个大整数比如 64 位 ID某些客户端库在反序列化时如果设置大了会走精度损失的路径导致返回结果错误。我就在一个项目里遇到过用 JavaScript 客户端调 Python 服务端返回的 ID 超过Number.MAX_SAFE_INTEGER结果解析后数字精度丢失。这个不是性能问题但会在优化过程中纠缠你很久提前规避能省很多排查时间。4. 超时、重试与并发控制让系统在不稳定里保持稳定的关键4.1 超时策略预设合理的临界等待值MCP 的调用方比如 Agent 主进程经常会因为等待响应而挂起如果服务端偶尔出现毛刺延迟客户端没有合理的超时机制整个 Agent 流程会被一个慢工具拖垮。我总结了三个超时必须分开设置连接超时、读超时、总请求超时。连接超时connect timeout建议 2-3 秒长了没意义读超时read timeout建议按业务特征来比如工具是查询类建议 5 秒如果是生成类比如内部调用大模型建议 30 秒以上。总请求超时一般设置在 30 秒左右。但超时设置有个坑超时时间不能一刀切。不同的 MCP 工具时延特征完全不同比如查询数据库可能 20ms但渲染一个复杂图表可能要 3 秒。统一设一个 5 秒的超时会导致快速工具被毛刺拖累慢工具频繁超时重试。合理的做法是MCP 服务端用timeout字段注册每个工具的超时元数据客户端根据工具的timeout参数动态设置 HTTP 层超时。4.2 重试策略指数退避和抖动MCP 运行在分布式环境里网络抖动、服务端临时过载、连接被中途关闭都是常态。没有重试机制的调用方一次偶发抖动就会导致整个 AI Agent 任务失败。但重试机制设计得不好也容易引发雪崩——服务端已经过载了客户端还在拼命重试只会让服务端彻底崩溃。我在这块踩过很深的坑总结下来几个原则限制最大重试次数。我一般设为 2-3 次超过就放弃让上层 Agent 决定是换路径还是最终报错。无限制重试是灾难。指数退避 抖动。每次重试的等待时间按指数增长比如第一次等 200ms第二次 400ms第三次 800ms并加上随机抖动比如 ±40%。抖动这个小细节非常重要。如果不加抖动100 个失败请求同时重试还是会把服务端打垮。加了随机抖动重试请求就会分散到不同时点服务端能缓过来。区分重试类型。不是所有错误都适合重试。HTTP 408、429、500、502、503、504 这几个状态码值得重试其中 429 和 503 表明服务端过载优先考虑退避但 4xx参数错误、方法不存在就不该重试重试多少次都没用只是浪费资源。JSON-RPC 层面的错误码也要注意parse error、invalid request属于必不能重试的internal error和server not initialized可以重试但需要退避。4.3 并发漏斗控制请求的瞬时冲击面MCP 服务端的并发处理能力和普通的 Web API 有显著区别。普通 Web API 的用户操作是自然稀疏的但 MCP 服务端的调用方是一个 AI Agent可能在同一个决策周期里发出 5-10 个并发请求。如果不对并发做控制服务端很容易被这种尖峰冲击。我在服务端入口加了一个简单的信号量Semaphore限流控制同一时刻最多处理的 MCP 请求数。一开始我拍脑袋设了个CPU 核数 * 4后来发现不合适——因为 MCP 请求大部分是 IO 密集的而且服务端内部往往还有下游调用线程数设太高只是制造上下文切换。后来我按服务端到下游单链路的平均延迟 * 预期 QPS来估算合理的并发级别假设服务端内部要等下游平均 50ms而我们要支持 200 QPS那至少需要50ms * 200 10个并发槽位才能保证不排队。这个方法比盲目设数字靠谱得多。注意这算的是稳定态如果算上毛刺和瞬时峰值再乘个 1.5-2 的安全系数。客户端侧同样要做并发控制。Agent 框架里经常有并行工具调用的能力但 SDK 层如果无脑并发很容易打爆服务端。常见的做法是给客户端 SDK 配一个请求队列例如工作队列最大 100 个任务同时在应用层用asyncio.Semaphore(20)限制最大并发工具调用数。5. 服务端关键路径优化裁掉每一个可以裁掉的等待5.1 从同步阻塞到异步化的改造MCP 服务端的业务逻辑往往不只是查个数据库这么简单实际项目中一个 MCP 工具可能内部要串行调用好几个下游系统比如先查权限、再查主数据、再查相关策略。如果这些下游调用是同步串行的那总耗时就是所有下游调用耗时的加总。在 Python 的 FastAPI 里这个问题最常见。FastAPI 默认的def端点是在线程池里跑的如果内部用了同步库一个请求会占据一个线程。每来一个请求如果线程池不够大请求就会排入等待队列。我搞过一个项目线程池默认 40100 并发打上来后面 60 个请求全在排队每个请求虽然逻辑只要 50ms但排队时间可能高达几百毫秒。改造方向很明确把服务端改造成纯异步async def且内部所有 IO 操作全部用异步库httpx.AsyncClient、aiosqlite、aioredis。改造完成后单节点同等配置下并发能力直接翻了 5 倍。但异步化有一个隐含的前提业务逻辑不能有 CPU 密集的同步计算。如果你的工具内部跑了一段耗时的正则匹配或者加密解密最好把它丢到独立线程池loop.run_in_executor或者进程池里避免阻塞事件循环。5.2 缓存策略从每次都算到大部分直接返回很多 MCP 工具是幂等查询类工具比如获取用户资料查询订单状态。这类工具的内部逻辑稳定只是数据会变化。如果不做缓存每次调用都打到下游延迟高不说还会给下游造成不必要的压力。我常用的缓存策略是分层缓存第一层进程内缓存TTL 短。用本地内存字典加 TTL比如 10-60 秒用于高 QPS 的热点查询。这层的命中率通常很高因为 AI Agent 在同一个会话里可能会多次查同一个实体比如先查天气再问明后天呢又会查一次同一个城市。注意进程内缓存在多实例部署时会有一致性问题所以 TTL 必须设短。第二层分布式缓存。如果服务端是多实例部署可以在本地缓存不命中时查 Redis 等集中式缓存。TTL 可以设长一些比如 5 分钟。这种层级的核心收益是减少下游核心系统的调用量。缓存是性能优化里性价比最高的一招。我经历过一个案例一个查询订单的 MCP 工具内部需要调三个下游接口拼数据单次耗时 400ms。加了 30 秒 TTL 的本地缓存后命中时直接 5ms 返回整体平均延迟从 400ms 降到了 18ms——因为热点查询全被缓存顶住了。但缓存有一个必须警惕的坑一致性。MCP 工具如果还涉及写操作比如修改备注下单必须在写操作后主动失效相关缓存。比如执行创建订单后立刻把该用户相关的订单查询缓存清掉避免用户刚下完单、再问我的订单时查到了旧数据。5.3 服务端事件循环和调度优先级的处理这块比较细但如果你的服务端延迟敏感值得关注。在异步服务端里事件循环里除了 MCP 请求的解析和响应还有心跳、日志、指标上报等任务。如果这些任务处理不当会和核心请求抢事件循环的时间片。我做的优化是把非核心的开销全部移出请求关键路径。日志走异步队列绝不在事件循环里同步写文件指标上报用单独的周期任务批量发送JSON-RPC 的响应构造结束后可以异步记录审计日志而不是同步等待日志写完再返回。这一步单个请求省下的时间可能只有 1-2ms但在高并发下堆积起来能减少事件循环的繁忙程度降低请求延迟的 P99 分位数。有个很反直觉的情况启用 debug 日志反而会让延迟整体变高。很多服务端框架在 debug 级别下会对每个请求打印全量 request/response 内容字符串拼接和格式化开销在 MCP 的短消息场景下是巨大的。我在项目里把日志级别统一设为 INFO结果 P99 直接降了 40% 左右。6. 真实案例复盘一次 MCP 延迟从 1200ms 到 250ms 的完整调优过程6.1 原始架构与瓶颈定位我在模拟项目X里实现了一个 MCP 工具服务功能是提供订单数据分析与建议。初版架构是客户端某个 Agent 编排框架通过 Streamable HTTP 方式连接服务端FastAPI 同步的 SQLAlchemy ORM 查询 外部推荐服务调用传输HTTP/1.1未开启 keep-alive 优化每次新连接压测 200 并发平均延迟 1200msP99 逼近 4 秒。第一件事不是改代码而是先做链路剖析用分层打点在客户端、服务端入口、服务端业务逻辑、下游调用处分别打时间戳定位延迟构成。实测结果环节平均耗时客户端发起请求到服务端接收含连接握手150ms服务端 JSON 反序列化12ms服务端内部认证和权限检查8ms订单查询SQLAlchemy 同步调用 MySQL300ms外部推荐服务调用同步 HTTP400ms结果拼装 JSON 序列化15ms响应传输回客户端260ms其他框架开销、线程调度55ms看完这张表方向就清楚了响应传输那块 260ms 是因为响应体特别大把分析建议的长文本 60KB 全量返回了外部推荐服务 400ms 是链路里的最大单点SQL 查询 300ms 也有优化空间。6.2 逐层优化的操作细节第一步针对响应体压缩开启 gzip并把返回内容裁剪掉不必要的调试字段和过长的背景说明。优化后传输时间从 260ms 降到 30msJSON 序列化体积从 60KB 降到 12KB。第二步针对外部推荐服务分析业务后发现推荐结果在 5 分钟内基本不变直接加了一层本地缓存LRU TTL300s。命中时外部服务调用时间从 400ms 降到 2ms。这个改动最激进效果也最明显。第三步针对 SQL 查询给相关表加了覆盖索引并且用异步 SQLAlchemy 替代同步版本通过asyncio配合aiomysql。查询从 300ms 降到 45ms且并发能力大幅提升。第四步连接优化客户端httpx.AsyncClient开启连接池limitshttpx.Limits(max_connections50, max_keepalive_connections20)服务端启用 Asyncio 模式并开启 keep-alive。连接握手时间从 150ms 降到 15ms 左右。第五步客户端并发控制在 Agent 调用层加了一个asyncio.Semaphore(30)避免瞬时 200 个并发全打上去服务端不会被打爆整体反而是稳定变快了。优化后的实测数据指标优化前优化后平均延迟1200ms245msP95 延迟2200ms410msP99 延迟4000ms680ms服务端 CPU 占用率85%35%最大并发支持数约 100800未继续加压请求成功率96%99.9%这个结果说明一个问题延迟优化往往不是做一个大动作而是把几个中等动作叠加起来每个省 50-200ms最后才能得到数量级的改善。单一环节的优化天花板有限全链路优化才能撼动分水岭。6.3 回滚和灰度的一点心得优化过程中的每一步我都做了单独压测确认没问题才合并。有一个特别重要的习惯每次改动单独跑一次 AB 对比对比 P50/P95/P99 三个指标。只看平均延迟会被极端值带偏只看 P99 又会掩盖整体变慢的问题。三指标一起看才能判断改动到底是整体变快还是把长尾砍掉了但平均没变。另外一个坑缓存 TTL 设太短等于没加缓存设太长又怕数据过期导致下游指令算错。我当时的经验是对于建议类、资料类数据TTL 可以放开到 5-10 分钟对于状态类、余额类数据TTL 不超过 10 秒。而且最好做主动失效——下游有变更事件时通过消息通道主动清除对应缓存。7. 监控、日志与持续优化机制性能调优不能一锤子买卖7.1 全链路追踪和关键指标采集性能调优做完一轮后最怕的是环境发生变化下游变慢、流量翻倍、数据量上涨导致性能回退。所以监控体系必须同步建设起来我建议重点盯这几个指标请求耗时分布P50/P95/P99这是体验的核心指标工具维度拆分耗时每个具体 MCP 工具的延迟方便定位是谁拖后腿传输与序列化开销占比如果这个占比长期在 30% 以上说明还有优化空间连接池命中率连接复用的比例太低说明 keep-alive 没生效服务端事件循环的繁忙度比如 asyncio 的 loop 利用率超过 70% 就要小心了全链路追踪Trace的实现上我建议在 MCP 消息的初始化阶段塞入一个trace_id放到 JSON-RPC 的扩展字段比如_meta.trace_id里在服务端和所有下游调用里传递这个 ID。这样就能把所有环节的耗时串成一条完整的链路定位瓶颈时不用靠猜。7.2 性能回归测试与持续压测我在项目里整理了一套MCP 性能回归脚本用固定的测试负载集比如 10 个常用工具的混合调用在每次发布新版本时自动跑一遍对比耗时基线的偏差。偏差超过 15% 就报警。这个机制的意义在于MCP 服务端的性能往往是温水煮青蛙式劣化的比如某次加了个日志、某个依赖库升级后变慢 10ms单个 commit 看不出来但累积起来就会把之前的优化成果全部吞掉。回归测试的负载集合不用太花哨关键是贴近真实使用。我用的是一段模拟真实 Agent 对话流程的脚本先调用查询订单再串行调用获取推荐再并行调用三个查询资料工具。这样的混合负载能反映真实场景的流量特征。7.3 遇到过的几个假优化和负优化在持续优化的过程中有一些改动看起来是优化实则是负优化我列出来供参考把连接池调得过大。连接池的上限不是越大越好。每条连接都要占据文件描述符、内存和系统资源而且连接太多会导致 TLS 握手风暴如果服务端并发接收太多新连接。有一次我把连接池上限从 50 调到 500压测下平均延迟反而上升了 8%——因为系统在管理大量空闲连接。把线程池调得过大。线程池太大CPU 时间都花在线程切换上。线程池的合理规模应该和 IO 等待占比挂钩IO 密集可以稍微大一点计算密集一定要小。无脑开 HTTP/2。HTTP/2 确实好但如果客户端和服务端之间有老版本的负载均衡器或者代理可能对 h2 的支持不完整反而引入诡异的连接中断问题。要先确认中间链路完整支持再切换。把 so 亲和性设置得太死。有些优化方案建议把服务端进程绑定到特定 CPU 核但如果对 NUMA 架构理解不深容易导致内存访问跨节点性能反而下降。这个操作没有十足的把握不要在生产环境乱上。这些都是经验教训。性能调优最怕的不是不知道怎么做而是做了自以为对的事结果适得其反。每次改动后都要回归验证这个习惯比任何优化技巧都值钱。最后再分享一个小技巧如果你用的是 Python 的 MCP SDK不管是 client 还是 server可以关注一下底层是否依赖了anyio和httpx的版本这两个库的版本更新经常带来性能改善尤其是httpx从 0.23 切到 0.27 之后连接池的内部调度逻辑优化了很多同样的代码性能就有提升。我遇到过几次什么都没改升级依赖后延迟下降 10%的情况很意外但也说明一个道理有时候性能问题的答案不在你自己的代码里而在你依赖的底层 SD 里。多试试升级依赖用版本号排除一下也是一种性价比很高的调优思路。