协议
从 Socket、TCP、HTTP、SSE 与 WebSocket,一路拆到 RPC、JSON-RPC 和 MCP;把分层、连接、流式传输、超时重试与 Agent 工程串成一条线。
目录 · 76 节
- 1. 协议分层与职责边界
- 1.1 远程MCP调用链路
- 1.2 分层故障定位
- 2. Socket:应用访问网络协议栈的接口
- 2.1 Socket不是TCP
- 2.2 TCP没有消息边界
- 2.3 连接失败与业务结果不确定性
- 2.4 TCP三次握手的确认范围
- 2.5 TCP关闭、半关闭与连接状态
- 2.6 阻塞、非阻塞与I/O多路复用
- 2.7 TCP Keepalive与应用心跳
- 3. HTTP:统一的资源与请求响应语义
- 3.1 HTTP版本的主要差异
- 3.2 Safe、Idempotent与可重试语义
- 3.3 HTTPS请求延迟构成
- 3.4 连接池容量边界
- 3.5 HTTP缓存、验证与并发更新
- 3.6 Proxy与Gateway的超时边界
- 3.7 CORS与认证不是一回事
- 4. SSE:基于HTTP的服务端事件流
- 4.1 自动重连不等于消息可靠
- 4.2 SSE容量估算
- 4.3 SSE事件语义设计
- 4.4 断联后的连续回放
- 4.5 浏览器EventSource的工程限制
- 5. WebSocket:双向长连接消息
- 5.1 握手、Frame与心跳
- 5.2 扩容与背压
- 6. RPC:远程调用抽象
- 6.1 gRPC
- 6.2 JSON-RPC
- 6.3 JSON-RPC错误模型
- 6.4 REST、gRPC与JSON-RPC的选择
- 7. MCP:AI应用的上下文与能力协议
- 7.1 Host、Client与Server角色
- 7.2 工具调用链路
- 7.3 Tools、Resources和Prompts的控制权
- 7.4 MCP与Function Calling的关系
- 7.5 stdio与Streamable HTTP
- 7.6 协议版本差异
- 7.7 Progress、取消与长任务
- 7.8 远程MCP授权与安全
- 7.9 MCP错误分层
- 8. Agent项目中的协议组合
- 8.1 一次“查支付服务错误日志”的调用
- 8.2 协议级指标与Dashboard
- 9. 超时、重试、幂等、背压与取消
- 9.1 Deadline预算传播
- 9.2 暂态错误重试
- 9.3 背压需要有界缓冲
- 9.4 取消是跨层协作
- 9.5 幂等键设计
- 9.6 熔断、限流与隔离
- 9.7 大型结果传输
- 10. 典型故障定位
- 10.1 SSE每60秒准时断一次
- 10.2 gRPC P99高但服务端Handler很快
- 10.3 MCP Tool偶发重复执行
- 10.4 HTTP 200但Agent仍报工具失败
- 10.5 同一个MCP Server本地正常、远程失败
- 10.6 大量TIME_WAIT连接
- 11. 协议选型摘要
- 12. 高频追问
- HTTP是基于TCP吗
- SSE与长轮询有什么区别
- SSE与WebSocket谁性能更高
- RPC一定比REST快吗
- MCP为什么用JSON-RPC而不是直接REST
- MCP和API Gateway是什么关系
- 连接复用与会话是什么关系
- 为什么TCP可靠,应用还会丢消息
- HTTP/2多路复用为什么还有队头阻塞
- Deadline和Timeout有什么区别
- MCP Server无状态以后,长任务放哪
- 13. 分层总结
- 参考规范
协议
Socket、HTTP、SSE、RPC和MCP经常出现在同一问题中,但它们位于不同抽象层级,不能作为五种平级方案直接比较。分析时应先区分传输接口、网络协议、交互模式、调用抽象和Agent能力协议。
先把主线立住:
Socket是操作系统提供的通信端点API;TCP或QUIC负责传输;HTTP定义请求响应语义;SSE和WebSocket定义更具体的通信方式;RPC把消息交换抽象成远程方法调用;MCP用JSON-RPC规定AI应用与能力提供方如何发现和调用工具、资源与提示模板。

1. 协议分层与职责边界
| 概念 | 所在层次 | 主要解决什么 | 不负责什么 |
|---|---|---|---|
| Socket API | 操作系统编程接口 | 创建端点并读写网络数据 | 不定义业务消息含义 |
| TCP | 传输协议 | 可靠、有序的字节流 | 不保留应用消息边界 |
| QUIC | 基于UDP构建的传输协议 | 可靠流、多路复用、连接迁移 | 不直接定义业务API |
| HTTP | 应用层协议 | Method、URI、Header、Status、Body及缓存语义 | 不等于某一种底层连接 |
| SSE | HTTP上的事件流格式与浏览器API | Server → Client持续推送文本事件 | 不提供原生双向消息通道 |
| WebSocket | 双向消息协议 | 长连接上的全双工文本或二进制消息 | 不自带业务幂等和回放 |
| RPC | 调用抽象 | 把请求、响应、序列化和错误包装成远程方法 | 不等于某个固定传输协议 |
| JSON-RPC | RPC消息协议 | 用JSON定义Request、Response、Notification和Error | 不规定必须走HTTP |
| MCP | AI能力交互协议 | 能力发现、工具调用、资源、Prompt及扩展 | 不是新的TCP,也不是模型推理协议 |
一层可以有多种实现,上层也不必与下层一一绑定。HTTP/1.1和HTTP/2通常运行在TCP上,HTTP/3运行在QUIC上;SSE运行在HTTP上;gRPC通常使用HTTP/2;JSON-RPC既能走HTTP,也能走stdio或别的双向通道;MCP目前把JSON-RPC消息承载在stdio或Streamable HTTP上。
1.1 远程MCP调用链路

假设Agent需要调用远程MCP Server的search_logs工具,完整链路不能简化为一次HTTP请求,而应按以下层次分析:
- Host根据URL取得域名,先查浏览器、进程或系统DNS缓存;未命中再向Resolver查询A/AAAA记录;
- 得到IP后建立TCP连接,或者为HTTP/3建立QUIC连接;
- HTTPS继续执行TLS握手,校验证书链、有效期、主机名和信任根,并协商应用协议;
- HTTP层发送
POST /mcp,代理或网关可能在此完成认证、限流、路由和Body限制; - MCP Server解析JSON-RPC,用
id关联请求,按method分发到tools/call; - Executor校验Tool Schema、调用方身份、Scope、租户、资源范围和审批策略;
- Tool访问Loki、数据库或第三方API,真正的业务副作用在这一层发生;
- Result沿原路返回,Host写Trace,再决定把哪些Observation放回模型上下文。
每一层的“成功”都只覆盖自身边界:DNS成功仅表示获得地址,TCP成功仅表示连接到某个端点,HTTP 200仅表示HTTP交换成功,JSON-RPC Result也不保证工具返回的业务内容正确。因此,“调用成功返回200”不能作为完整的业务成功判定。
1.2 分层故障定位
| 现象 | 优先检查层 | 典型证据 |
|---|---|---|
| 域名偶发无法解析 | DNS | Resolver耗时、缓存TTL、NXDOMAIN、A/AAAA结果 |
connection refused | 传输与监听端口 | IP、Port、Listener、Security Group、RST |
| Connect Timeout | 路由或防火墙 | SYN重传、NAT、Egress规则、丢包 |
| TLS握手失败 | TLS | SNI、证书链、SAN、有效期、ALPN、系统时间 |
| HTTP 401/403 | 认证与授权 | Token Audience、Scope、Policy Decision |
| HTTP 502/504 | Gateway与上游 | Upstream Connect、Response Header Timeout、网关日志 |
JSON-RPC -32601 | 协议Method | 协议版本、Capability、Method名称 |
| Tool返回业务错误 | Tool Backend | 参数、依赖状态、业务错误码、Trace Span |
| 客户端超时但服务端产生了副作用 | 分布式结果未知 | Idempotency Key、业务状态查询、服务端审计记录 |
排错顺序通常从下往上,但业务证据从上往下关联:先确认域名、连接、TLS、HTTP,再看JSON-RPC与Tool;同时用同一个Trace ID把网关、MCP Server和Tool Backend串起来。
2. Socket:应用访问网络协议栈的接口
Linux中的socket()创建通信端点并返回文件描述符。对TCP服务端,典型调用链是:
socket → bind → listen → accept → read/write → close
客户端通常是:
socket → connect → read/write → close
2.1 Socket不是TCP
Socket是API和内核对象,TCP是协议。创建SOCK_STREAM通常使用TCP,创建SOCK_DGRAM通常使用UDP;Unix Domain Socket也使用Socket API,却根本不经过IP网络。因此“HTTP底层是Socket”只能表示HTTP实现最终借助Socket API收发数据,不能把Socket当成HTTP的直接下层协议。
2.2 TCP没有消息边界
TCP只提供有序字节流。发送方连续两次write(),接收方可能一次read()全部读到,也可能拆成多次读到。这不是粘包Bug,而是应用错误地假设TCP替自己保留了消息边界。
应用必须自行Framing,常见方法有:
- 固定长度;
- 分隔符,例如stdio版MCP中的换行分隔;
- Length Prefix,例如前4字节表示消息长度;
- 自描述格式,例如HTTP Header中的
Content-Length或Chunked Encoding; - 协议帧,例如HTTP/2 Frame和WebSocket Frame。
2.3 连接失败与业务结果不确定性
TCP断开只能证明该通信路径已经失效,不能证明服务端未执行请求。客户端发出扣款请求后连接断开,可能是请求未到达,也可能是扣款成功但响应丢失。这种“结果未知”是重试与幂等机制存在的根本原因。
2.4 TCP三次握手的确认范围
TCP三次握手可以简化为:
Client Server
| -------- SYN(x) --------> |
| <---- SYN(y), ACK(x+1) --- |
| -------- ACK(y+1) -------> |
| ESTABLISHED |
它建立双方的初始序列号并确认双向收发能力。为什么不是两次?如果服务端只收到SYN就进入完整连接状态,网络中延迟到达的旧SYN可能制造无效连接;第三次ACK让服务端知道客户端确实收到了它的初始序列号。
三次握手成功仍不代表应用健康:端口可能由错误进程监听,TLS可能随后失败,应用线程池也可能已经耗尽。因此监控要拆开dns_ms、connect_ms、tls_ms、ttfb_ms和完整请求耗时。
2.5 TCP关闭、半关闭与连接状态
TCP两个方向独立关闭,所以正常关闭通常表现为双方分别发送FIN和ACK。主动关闭方进入TIME_WAIT,避免旧报文污染后续使用相同四元组的新连接,并保证最后ACK丢失时还能重发。大量短连接会产生大量TIME_WAIT,应优先使用连接复用和正确配置的连接池,而不是直接修改内核参数。
CLOSE_WAIT表示对端已经发送FIN,本端内核也已通知应用,但应用尚未调用close()。大量CLOSE_WAIT通常指向文件描述符泄漏、异常路径未释放Response Body,或协程长期阻塞;它与TIME_WAIT属于不同问题。
半关闭表示一个方向已经不能继续发送,另一个方向仍可传输。例如客户端使用shutdown(SHUT_WR)通知服务端请求Body已经发送完毕,同时仍可继续读取响应。应用协议通常会封装结束边界,但理解半关闭有助于解释代理和流式连接中的异常断联。
2.6 阻塞、非阻塞与I/O多路复用
阻塞Socket在数据未就绪时使调用线程等待;非阻塞Socket立即返回EAGAIN,由程序稍后重试。select、poll、epoll或语言Runtime统一管理大量文件描述符的就绪事件,因此SSE或WebSocket无需为每条连接分配一个操作系统线程。
但I/O非阻塞不等于业务处理不会阻塞。在事件循环中直接执行大型JSON解析、Embedding或慢数据库查询,仍会使其他连接无法及时获得执行机会。常见结构是:
Event Loop:收发与轻量协议解析
Bounded Worker Pool:CPU或阻塞任务
Bounded Queue:显式制造背压
Cancellation:连接关闭或Deadline到期后停止无用工作
2.7 TCP Keepalive与应用心跳
TCP Keepalive由内核探测空闲连接是否仍然存活,默认周期通常较长;应用心跳携带协议语义,可以更快发现代理Idle Timeout、推动SSE Flush,或确认对端Event Loop仍在正常调度。二者可以同时使用,但不能相互替代。
- TCP Keepalive:连接级、内核实现、对业务不可见;
- WebSocket Ping/Pong:协议控制帧;
- SSE注释行
: heartbeat\n\n:应用约定,用于保活和Flush; - 业务心跳:可能包含
last_event_id、负载或租约信息。
3. HTTP:统一的资源与请求响应语义
HTTP是无状态的应用层协议。一次请求包含Method、Target、Header和可选Body,一次响应包含Status、Header和可选Body:
POST /incidents/inc-7/cancel HTTP/1.1
Host: agent.example.com
Content-Type: application/json
Idempotency-Key: cancel-inc-7-v1
{"reason":"user_requested"}
HTTP/1.1 202 Accepted
Content-Type: application/json
{"incident_id":"inc-7","status":"cancelling"}
“HTTP无状态”是说单个请求的语义不依赖它恰好复用了哪条连接,不是说业务不能有Session。登录态可以放Cookie、Token或服务端Session Store,只是它们属于应用状态。
3.1 HTTP版本的主要差异
| 版本 | 传输与消息组织 | 关键特征 | 常见误区 |
|---|---|---|---|
| HTTP/1.1 | 通常一个TCP连接承载顺序消息 | Keep-Alive、Chunked、Pipeline语义 | Keep-Alive不等于永不关闭 |
| HTTP/2 | TCP上的二进制Frame与多个Stream | 多路复用、Header Compression、流控 | 多路复用仍会受TCP丢包影响 |
| HTTP/3 | QUIC上的HTTP语义 | 独立流、连接迁移、减少传输层队头阻塞 | HTTP/3不是“HTTP改走UDP就不可靠” |
HTTP/2把多个逻辑Stream复用在一条TCP连接上,避免HTTP/1.1应用层排队,但底层TCP丢包时,后续字节仍需等待重传。HTTP/3借助QUIC让不同流在传输层更独立。
3.2 Safe、Idempotent与可重试语义
- Safe表示客户端没有请求修改服务端状态,GET和HEAD属于Safe Method;
- Idempotent表示相同请求执行一次或多次,期望的服务端效果相同;PUT、DELETE和Safe Method在HTTP语义上是幂等的;
- POST默认不幂等,但业务可以用Idempotency Key把它做成可安全重试;
- 幂等只约束请求的预期效果,服务端每次都记访问日志并不违反幂等。
客户端遇到连接中断时,不能见请求就重试。读请求通常可以指数退避重试;写请求必须确认方法语义或携带幂等键,并让服务端持久化key → request_hash → result。
3.3 HTTPS请求延迟构成
冷连接的粗略时间可以拆成:
| 符号 | 含义 | 常见优化 |
|---|---|---|
| 域名解析 | 本地缓存、合理TTL、稳定Resolver | |
| TCP或QUIC建连 | 连接池、就近接入、Happy Eyeballs | |
| TLS握手和证书校验 | Session Resumption、连接复用 | |
| 网关与业务处理到首字节 | 索引、缓存、并发控制、下游Deadline | |
| Response Body传输 | 压缩、分页、流式返回、减少Payload |
例:DNS 20 ms、Connect 35 ms、TLS 40 ms、服务端120 ms、传输15 ms,总耗时约230 ms。若复用已有HTTP/2连接,前三段可能大幅下降;因此只观察服务端Handler的120 ms,会遗漏接近一半的用户感知延迟。
实际观测还要区分TTFB和完整响应时间:TTFB低但Body下载很慢,多半不是业务计算;SSE的“完整响应时间”近似无限,更应该看首事件延迟、事件间隔、断线率和积压量。
3.4 连接池容量边界
HTTP Client连接池复用TCP/TLS连接,减少握手成本。核心参数包括:
- 每个Host的最大空闲连接与最大总连接;
- Idle Connection Timeout;
- Response Header Timeout和整体Request Deadline;
- HTTP/2每连接允许的并发Stream;
- DNS变化后旧连接的淘汰策略。
连接池过小会在Client侧形成排队,连接池过大则可能使下游数据库或服务过载。连接池解决传输资源复用问题,不能提供无限吞吐。生产指标至少应区分“等待连接耗时”和“获得连接后的请求耗时”,否则无法根据P99上升准确定位瓶颈。
3.5 HTTP缓存、验证与并发更新
Cache-Control决定响应能否缓存以及多久新鲜;ETag和Last-Modified是Validator。缓存过期后,客户端可以带If-None-Match重新验证:
GET /mcp/tool-catalog HTTP/1.1
If-None-Match: "catalog-v42"
未变化时服务端返回304 Not Modified,客户端复用本地Body。MCP 2026版把可发现信息设计得更适合缓存,但Tool结果能否缓存仍由业务语义决定;表面表现为读操作的请求也可能包含租户或权限差异,不能据此直接缓存。
If-Match还能做乐观并发控制:客户端只在资源版本仍等于某个ETag时更新,否则返回412 Precondition Failed。它解决Lost Update,不等于数据库事务。
3.6 Proxy与Gateway的超时边界
一次HTTP请求可能经过CDN、WAF、Ingress、API Gateway、Service Mesh Sidecar和业务服务。每一跳都可能有:
- Connect Timeout;
- Response Header Timeout;
- Idle Timeout;
- Request/Response Body大小限制;
- 重试策略;
- Buffering和压缩;
- Header改写与认证。
SSE常见故障包括:业务持续写入事件,但代理在Buffer达到阈值后才发送;或者服务端允许连接持续30分钟,而Ingress在60秒无字节传输后断开连接。排查时需要确认超时发生的具体链路,并使心跳间隔小于链路中最短的Idle Timeout。
3.7 CORS与认证不是一回事
CORS是浏览器执行的跨源访问策略,不是服务端认证机制。服务端即使返回Access-Control-Allow-Origin: *,仍然可以要求Bearer Token;反过来,Token正确但Preflight没通过,浏览器依然不会把实际请求交给页面。
带Cookie的跨源请求不能使用通配Origin。远程MCP和SSE端点应使用明确的Origin Allowlist,并独立校验Token的Issuer、Audience、Scope和资源归属。非浏览器客户端直接调用接口时不受CORS约束,因此安全边界必须由服务端实现。
4. SSE:基于HTTP的服务端事件流
SSE使用text/event-stream。客户端先发普通HTTP请求,服务端返回响应头后不断追加UTF-8文本事件:
id: 43
event: agent_done
retry: 3000
data: {"incident_id":"inc-7","agent":"metrics"}
空行表示一个事件结束。浏览器EventSource在连接断开后可以重连,并通过Last-Event-ID告诉服务端最后处理到哪里。
4.1 自动重连不等于消息可靠
Last-Event-ID只是续传游标。服务端若没有保留事件,仍然无从补放。AgentOps这类长任务应做到:
持久事件表 / Redis Stream:负责回放
SSE连接:负责实时投递
Event ID:负责排序、续传与客户端去重
任务状态机:负责最终事实
断开SSE连接不能直接取消Worker;显式取消应通过独立HTTP API发起并传播Cancellation。连接是观察通道,任务是业务实体;若将两者生命周期绑定,短暂的客户端网络切换也会错误终止后台任务。
4.2 SSE容量估算
用Little’s Law估一个粗略并发量:
| 符号 | 含义 |
|---|---|
| 平均同时存活的SSE连接数 | |
| 平均每秒新建的连接数 | |
| 一条连接平均存活秒数 |
例如每秒启动20个诊断任务,每个页面平均观察5分钟:
这只是数量级估算。真实容量还受文件描述符、内核Buffer、TLS、代理Idle Timeout、心跳频率、单连接事件速率和慢客户端背压影响。服务端不能让每条SSE连接占一个昂贵线程,也不能无限堆积待发送事件。
4.3 SSE事件语义设计
不要只发一串无法区分的message。事件类型应与任务状态机对齐:
task_created
agent_started
tool_started
tool_progress
tool_completed
agent_completed
task_completed
task_failed
每条事件至少携带event_id、task_id、event_type、occurred_at和最小Payload。事件ID必须在同一个可回放范围内单调且唯一;若不同Partition分别编号,应同时携带Partition或使用可比较的复合Cursor,不能将局部序号作为全局顺序。
进度事件可以合并,终态事件不能丢。客户端按event_id幂等应用事件,并以服务端任务快照作为最终事实。SSE是状态变化通知,不应成为唯一状态仓库。
4.4 断联后的连续回放
错误实现通常采用“先查历史,再订阅实时”的顺序。历史查询结束到订阅建立之间会形成事件丢失窗口。更稳妥的流程是:
1. 建立实时订阅并暂存新事件
2. 读取持久事件流当前 watermark
3. 回放 (Last-Event-ID, watermark]
4. 合并暂存区,按 event_id 排序去重
5. 切到实时流
若事件已经超过Retention,应返回resync_required,客户端重新获取任务快照,不要伪造连续性。至少一次投递加幂等消费通常比追求“绝不重复”更实际。
4.5 浏览器EventSource的工程限制
原生EventSource使用方便,但其API不允许像fetch一样任意设置请求Header。常见认证方案包括同源Cookie、短期签名URL,或使用基于fetch的SSE Client读取ReadableStream。长期Access Token不应放入Query,因为它可能进入代理日志和浏览器历史。
还要处理:
- 页面隐藏或网络切换后的重复连接;
- 多Tab同时订阅造成连接数放大;
- HTTP/1.1浏览器对同Origin并发连接的限制;
- 代理压缩或Buffering导致事件不能及时Flush;
- 服务端Shutdown时发送
server_draining并引导重连; - 客户端消费速度落后时的Queue上限和断开策略。
5. WebSocket:双向长连接消息
WebSocket先通过HTTP握手,随后在同一连接上交换独立的Text、Binary和Control Frame。客户端和服务端都能主动发消息,适合协同编辑、游戏、双向语音控制或高频交互。
| 需求 | SSE | WebSocket |
|---|---|---|
| 服务端持续推送,客户端偶尔用普通HTTP发命令 | 合适 | 能做,但更重 |
| 双方都需高频主动发消息 | 不合适 | 合适 |
| 浏览器自动重连 | EventSource提供基础能力 | 应用自己实现 |
| 消息回放 | 仍需服务端事件存储 | 仍需服务端事件存储 |
| 二进制消息 | 不擅长 | 原生支持 |
| HTTP代理与观测工具兼容性 | 通常较自然 | 需要确认Upgrade、Idle Timeout等配置 |
WebSocket是双向通道,不是可靠消息队列。断线重连、Sequence、ACK、去重、离线消息和背压都要由应用协议补齐。
5.1 握手、Frame与心跳
经典WebSocket通过HTTP/1.1 Upgrade握手,服务端返回101 Switching Protocols后进入WebSocket Frame协议。HTTP/2上的Extended CONNECT是另一套建立方式,不能把所有实现都解释成字面上的HTTP/1.1 Upgrade。
Frame分为Text、Binary和控制帧。Ping/Pong用于协议级保活和测量存活,Close Frame携带关闭码与原因。应用仍应定义消息Envelope:
{
"type": "tool_progress",
"message_id": "msg-91",
"sequence": 42,
"task_id": "inc-7",
"payload": {"percent": 80}
}
message_id用于去重,sequence用于发现缺口,task_id用于路由。WebSocket保证同一连接上的Frame顺序,不保证断线重连后的业务消息连续,也不保证消息已经写进Socket就被对端业务处理。
5.2 扩容与背压
长连接会落在某个实例上。若消息由其他实例产生,需要Redis Pub/Sub、Stream、Kafka或专用Realtime Gateway把消息路由到持有连接的节点。Sticky Session可以降低路由复杂度,却不能代替状态持久化;节点崩溃后连接仍要在别处恢复。
慢客户端会导致Send Buffer增长。服务端应限制单连接待发送字节数、消息数、单消息大小和最大停滞时间。超过阈值时,可以丢弃可合并进度、降低发送频率,或者使用明确的Close Code断开连接;无限Buffer可能导致内存耗尽。
6. RPC:远程调用抽象
RPC通常包含以下部分:
IDL / Schema
→ 生成或手写 Client Stub
→ 序列化 Method + Arguments + Metadata
→ Transport 发送
→ Server Dispatch
→ 执行业务逻辑
→ 返回 Result / Error
RPC只能在接口形式上接近本地函数。远程调用还涉及网络延迟、部分失败、超时、重试、序列化、版本兼容,以及服务端已执行但客户端未收到结果等问题。实现RPC时必须显式处理这些分布式系统语义。
6.1 gRPC
gRPC常用Protocol Buffers定义服务,并在HTTP/2上提供四种调用模式:Unary、Server Streaming、Client Streaming和Bidirectional Streaming。
service DiagnosisService {
rpc StartDiagnosis(StartRequest) returns (StartReply);
rpc WatchDiagnosis(WatchRequest) returns (stream DiagnosisEvent);
}
它适合内部强Schema、多语言服务、代码生成和高吞吐调用。每次调用应显式设置Deadline,并将剩余预算向下游传播。取消RPC后,业务代码也需要停止数据库查询或模型调用;框架发出取消通知,不代表后台协程会自动终止。
四种调用模式及其语义
| 模式 | 请求 | 响应 | 适合 |
|---|---|---|---|
| Unary | 一个 | 一个 | 普通查询与命令 |
| Server Streaming | 一个 | 多个 | 搜索结果、进度、模型Token流 |
| Client Streaming | 多个 | 一个 | 分片上传、批量聚合 |
| Bidirectional Streaming | 多个 | 多个 | 双向协作、实时控制 |
Streaming减少反复建调用的开销,也引入更长资源占用、流控、取消和部分结果语义。一个Server Stream中途失败时,客户端需要知道前面收到的数据能否保留、从哪个Cursor续传,不能只凭一个Unavailable决定全量重试。
Protobuf兼容性
Protobuf Wire Format用Field Number识别字段,而不是字段名:
message DiagnosisEvent {
string task_id = 1;
int64 sequence = 2;
string type = 3;
reserved 4, 5;
reserved "legacy_payload";
}
演进规则至少记住:
- 已上线字段不能改Number;
- 新增Optional字段通常Wire-safe,旧代码会忽略Unknown Field;
- 删除字段后应Reserve旧Number,不能重新赋予其他含义;
- 不要随意改变字段类型、拆进已有
oneof或重排Enum语义; - Wire兼容不等于业务兼容,新字段默认值可能改变旧客户端行为;
- ProtoJSON的兼容规则与二进制Wire Format并不完全相同。
gRPC错误模型与HTTP状态码的区别
常见状态包括INVALID_ARGUMENT、NOT_FOUND、ALREADY_EXISTS、PERMISSION_DENIED、RESOURCE_EXHAUSTED、UNAVAILABLE和DEADLINE_EXCEEDED。错误码必须表达调用者下一步能做什么:改参数、重新认证、稍后重试,还是查询最终状态。
只对明确暂态且幂等的调用自动重试。DEADLINE_EXCEEDED表示客户端不再等待,不证明服务端没执行;UNAVAILABLE也可能发生在请求已经送达以后。写RPC仍需要Idempotency Key或业务唯一约束。
6.2 JSON-RPC
JSON-RPC 2.0用jsonrpc、id、method和params表示请求:
{
"jsonrpc": "2.0",
"id": 17,
"method": "tools/call",
"params": {
"name": "search_logs",
"arguments": {"service": "payment", "minutes": 15}
}
}
响应使用相同id关联:
{
"jsonrpc": "2.0",
"id": 17,
"result": {"content": [{"type": "text", "text": "..."}]}
}
Request有id并期待Response;Notification没有id,接收方不返回Response。JSON-RPC规定消息形状和关联方式,不规定URL、HTTP Method、鉴权、连接复用或服务发现,因此它可以运行在HTTP、WebSocket、stdio等通道上。
6.3 JSON-RPC错误模型
协议错误放在error字段,不能同时返回result:
{
"jsonrpc": "2.0",
"id": 17,
"error": {
"code": -32602,
"message": "Invalid params",
"data": {"field": "minutes", "reason": "must be positive"}
}
}
-32700表示Parse Error,-32600表示Invalid Request,-32601表示Method Not Found,-32602表示Invalid Params,-32603表示Internal Error;Server自定义错误使用约定范围。面试要区分三层:
HTTP Error:请求没正常到达或被HTTP层拒绝
JSON-RPC Error:消息被解析,但协议调用失败
Tool Business Error:工具成功返回一个业务失败结果
例如,HTTP 200响应中可以包含JSON-RPC Error;HTTP 401发生时通常尚未进入JSON-RPC Method。若Trace和告警没有按层分类,鉴权失败、协议错误和Loki查询失败可能都被记录为tool_call_failed,从而降低故障定位效率。
6.4 REST、gRPC与JSON-RPC的选择
| 维度 | HTTP REST风格 | gRPC | JSON-RPC |
|---|---|---|---|
| 核心抽象 | Resource + Method | 强类型Service Method | Method + Params |
| Schema | OpenAPI可选或约定 | Proto强Schema与代码生成 | JSON Schema或文档约定 |
| 浏览器直连 | 最自然 | 通常需要gRPC-Web或Gateway | 可走HTTP,工具支持一般 |
| Streaming | SSE、Chunked、WebSocket等组合 | 四种原生RPC模式 | 取决于承载Transport |
| 缓存 | HTTP语义成熟 | 通常由应用或代理设计 | 协议本身不定义 |
| 内部多语言调用 | 可以 | 很合适 | 简单场景合适 |
| 动态工具生态 | 需要额外发现规范 | 偏静态契约 | MCP在其上补了发现与能力语义 |
不是所有内部调用都该改gRPC,也不是所有Tool都该改MCP。契约稳定、吞吐高、多语言的内部服务偏gRPC;公开资源API偏HTTP;需要动态发现并让Agent受控调用的能力偏MCP。
7. MCP:AI应用的上下文与能力协议
MCP采用Client–Server结构:Host中的MCP Client连接一个或多个MCP Server。Server可以暴露:
- Tools:可执行动作,例如查日志、调用API、写数据库;
- Resources:可读取的上下文,例如文件、Schema或知识对象;
- Prompts:可参数化的提示模板;
- Extensions:在基础协议之外协商的附加能力。
模型不会直接获得网络连接或系统权限。Host选择向模型暴露哪些能力,模型提出调用意图,受控Executor校验Schema、权限和审批后,MCP Client才把消息发给Server。
7.1 Host、Client与Server角色
- Host:用户实际使用的AI应用,管理模型、上下文、权限、UI和多个Client;
- MCP Client:Host内部与某一个Server建立逻辑关系的协议组件;
- MCP Server:向Client暴露一组Capability;
- Tool Backend:MCP Server背后真正执行查询或副作用的系统,它可能根本不知道MCP存在。
一个Host可以连接多个Server,通常每个Server对应独立Client实例和安全边界。Server返回的Tool Description、Resource内容和Annotation都属于不可信输入,Host不能因为数据来自协议层就直接写入System Prompt或免审批执行。
7.2 工具调用链路
用户问题
→ Host选择MCP Server与可见工具
→ Client获取或使用已缓存的工具Schema
→ 模型生成工具名与参数
→ Executor做参数、权限、预算与审批校验
→ MCP Client发送tools/call
→ MCP Server执行受控业务逻辑
→ 结构化Result返回
→ Host写入Trace并把结果交还模型
tools/list解决发现,tools/call解决调用;JSON Schema约束输入,结构化输出约束下游消费。Schema合法不表示业务安全,delete_repository即使参数完全正确,也仍然需要最小权限和人工确认。
一次调用至少应记录:
trace_id, request_id, protocol_version
server_name, tool_name, tool_schema_hash
arguments_hash, arguments_redacted
principal, tenant_id, granted_scopes, approval_id
started_at, deadline, latency_ms
jsonrpc_error, tool_error, retry_count
result_hash, result_uri, side_effect_id
JSON-RPC id负责单条协议请求的关联,Trace ID负责跨HTTP、MCP Server和Tool Backend追踪,Idempotency Key负责写操作去重。三个ID具有不同语义,不能使用同一个UUID替代全部标识。
7.3 Tools、Resources和Prompts的控制权
三类能力在语义和生命周期上存在明确差异:
| 能力 | 典型控制方 | 语义 | 项目例子 |
|---|---|---|---|
| Tools | Model-controlled | 执行动作并返回结果 | query_loki、restart_workload |
| Resources | Application-controlled | 按URI读取或订阅上下文 | Runbook、Schema、Trace Artifact |
| Prompts | User-controlled | 用户显式选择的参数化模板 | “生成事故复盘”模板 |
实际Host可以改变交互方式,但安全设计仍要遵守这个方向感:Tool可能产生副作用,需要Policy与Approval;Resource要做租户和版本过滤;Prompt内容也可能携带Prompt Injection,不能自动获得System级信任。
Tool的inputSchema约束输入,outputSchema可以约束结构化输出。调用成功后仍可能返回isError: true表示Tool执行错误;这与JSON-RPC Error不同,后者说明协议调用本身失败。这个区分对重试和评测非常重要。
7.4 MCP与Function Calling的关系
Function Calling是模型输出结构化调用意图的能力;MCP是Host与外部能力提供方之间的协议。典型链路是:
LLM Function Calling
→ Host / Executor
→ MCP Client
→ MCP Server
→ 实际 Tool / API
二者可以组合,也能各自单独存在。本地函数可以直接注册给模型而不走MCP;MCP Client也可以由确定性Workflow驱动,不必让模型决定每次调用。
7.5 stdio与Streamable HTTP
| 传输 | 进程关系 | 优点 | 代价 | 适用场景 |
|---|---|---|---|---|
| stdio | Client启动本地Server子进程 | 简单、低暴露面、继承本机权限边界 | 生命周期绑定本机,不适合远程共享 | IDE、本地文件与开发工具 |
| Streamable HTTP | Server是独立网络服务 | 可远程部署、鉴权、网关治理和多Client共享 | 需要处理网络安全、超时与扩缩容 | 团队级或平台级工具服务 |
Streamable HTTP使用单一MCP Endpoint。Client用POST发送JSON-RPC消息;Server可以直接返回JSON,也可以用SSE流返回多个消息。支持服务端流不等于每次请求都必须开SSE。
stdio模式中,每行是一个完整JSON-RPC消息,stdout只能写协议数据,普通日志必须写stderr。Host负责拉起子进程、关闭stdin、等待退出,必要时再Terminate。stdio省掉了网络鉴权,却继承Host进程的本机权限,因此工作目录、环境变量、可读Root和命令白名单更要收紧。
Streamable HTTP模式要回答四个问题:
- POST返回单个JSON Response还是
text/event-stream; - 长结果怎样分段、恢复和限制总大小;
- 负载均衡后请求是否能落到任意实例;
- Origin、Authorization、Protocol Version和Body限制由谁校验。
如果Server使用SSE返回多个消息,每个JSON-RPC消息仍必须完整地包含在一个SSE Event中,不能将不完整JSON作为“流式JSON”交给Client。网络流可以分块,但应用消息边界必须完整。
早期2024-11-05版本的HTTP+SSE传输已被Streamable HTTP替代。面试若只背“远程MCP就是一条SSE收、一条POST发”,说的是旧版本。
7.6 协议版本差异
MCP仍在快速演进,不能将某个SDK版本的行为视为稳定不变的协议规范:
| 协议版本 | 生命周期与会话重点 |
|---|---|
| 2025-06-18 / 2025-11-25 | initialize → initialized协商版本和Capability;HTTP模式可使用Session ID |
| 2026-07-28 | 协议核心改为无握手、无会话;版本、Client信息与Capability随请求携带,可选server/discover |
新版本的无状态核心允许任意实例处理自包含请求,更适合常规负载均衡、缓存和水平扩容。应用仍然可以维护长任务与持久状态,但这些状态不能仅保存在某条连接或单个Server实例的内存中。
版本兼容策略不应依赖解析失败后的推断:
- 明确携带
MCP-Protocol-Version; - 新Client可先调用
server/discover了解版本和Capability; - 按协议版本选择Codec和Method集合;
- Tool Schema缓存键包含Server Identity、Protocol Version和Schema Version;
- 收到未协商Capability的消息应拒绝,而不是尽力执行;
- 升级先做双版本Contract Test,再逐步切流。
7.7 Progress、取消与长任务
普通同步Tool Call适合在Deadline内完成的工作。长时间训练、代码分析或批量检索若一直占住HTTP请求,会遇到代理Timeout、重试歧义和资源占用。2026版MCP通过Tasks Extension支持异步生命周期:Server可以返回Task Handle,Client再查询状态、更新或取消。
项目实现可以抽象成:
tools/call
→ accepted + task_id
→ Worker持久化执行
→ tasks/get 查询状态
→ terminal result / error
→ TTL后清理结果
任务必须绑定Principal和Tenant,Task ID需要不可猜或有授权校验;取消是请求,不保证副作用能回滚;结果必须有TTL和大小上限。协议无状态不等于任务无状态,任务状态应放持久库或可靠队列,而不是某台MCP Server的Map里。
对于短调用,Progress Notification可以表明任务仍在执行,但实现仍应设置硬性最大Deadline,不能因为持续收到进度事件而无限延长任务生命周期。
7.8 远程MCP授权与安全
远程MCP的授权是HTTP传输层能力,通常围绕OAuth 2.0 / OIDC资源服务器模型。至少检查:
- TLS与Server Identity;
- Token的Issuer、Audience、有效期与Signature;
- Scope是否覆盖具体Tool和资源;
- Client Credential与签发Issuer绑定,防止凭据串用;
- Step-up Authorization只增加当前动作需要的Scope;
- Redirect URI和Authorization Response的
iss校验; - Tool调用的Tenant、对象归属与行级权限;
- 高风险写操作的Human-in-the-loop Approval。
认证回答“主体是谁”,授权回答“主体具备哪些权限”,Tool Policy回答“当前上下文是否允许执行”,Approval回答“高风险动作是否得到本次明确同意”。四个层次不能合并为单一isAdmin布尔值,否则无法表达上下文、资源和动作级权限。
MCP Server还要防SSRF、Prompt Injection、超大Result、恶意Tool Description、日志泄密和Confused Deputy。Host不应把用户Token原样转发给任意Server,也不能让Server诱导Host访问未经授权的另一个资源。
7.9 MCP错误分层
| 层 | 例子 | 是否适合重试 |
|---|---|---|
| Transport | DNS失败、连接重置、HTTP 502 | 只对暂态错误,受总Deadline约束 |
| Authorization | 401、Scope不足 | 刷新或Step-up后再试,不要盲重试 |
| JSON-RPC | Parse Error、Method Not Found | 修协议或版本,不重试同一错误消息 |
| MCP Capability | 未声明Tools、Method不属于该版本 | 重新发现或降级 |
| Tool Validation | 参数不满足Schema | 修参数 |
| Tool Execution | Loki超时、数据库冲突 | 看幂等性和业务错误类型 |
| Result Consumption | 输出过大、Schema不符、引用失效 | 截断、转Resource或标记Server缺陷 |
把错误分层后,告警才能带行动:tool_backend_unavailable可以重试和熔断,tool_permission_denied应查Policy,invalid_tool_arguments应改Planner或Schema,不能全部归咎于“模型不稳定”。
8. Agent项目中的协议组合
以AgentOps故障诊断为例,各协议各守一段边界:
Browser
├─ HTTP POST:创建、取消诊断任务
└─ SSE:接收可续传的诊断进度
Go API / Orchestrator
├─ 进程内函数:稳定、低成本的本地能力
├─ HTTP / gRPC:调用内部或第三方服务
└─ MCP Client:统一发现并调用可插拔AI工具
MCP Servers
├─ stdio:本地CodeGraph、工作区工具
└─ Streamable HTTP:共享的日志、指标、知识检索能力
Tool Backend
├─ HTTP API:Prometheus、Loki、外部平台
└─ Driver / Socket:PostgreSQL、Redis、Neo4j、Milvus
设计目标不是将所有接口统一改为MCP。稳定内部服务之间若已有成熟gRPC契约,没有必要再增加MCP封装;需要被多个Agent Host动态发现、隔离和治理的工具更适合使用MCP。浏览器使用SSE查看任务进度也不需要经过MCP。明确协议边界有助于故障定位。
8.1 一次“查支付服务错误日志”的调用
1. Browser POST /incidents 创建任务,拿到 incident_id
2. Browser建立 /events SSE,只负责观察
3. Planner选择 Experience Agent 与 search_logs Tool
4. Executor检查 Tool Allowlist、时间范围和租户
5. MCP Client调用 tools/call(request_id=17)
6. Log MCP Server把参数转换成受限Loki Query
7. Loki返回日志,Server做大小限制、脱敏和结构化
8. MCP Result关联 request_id=17 回到Host
9. Trace Recorder记录原始查询Hash、结果URI与Span
10. Worker写 task_completed 事件,SSE实时推送或稍后补放
如果第8步前连接断开,Client只知道结果未知。由于日志查询是只读,可以用同一参数和Deadline重试;如果Tool是restart_workload,则必须用incident_id + action + target + generation形成Idempotency Key,并先查询动作最终状态。
8.2 协议级指标与Dashboard
| 层 | 指标 |
|---|---|
| DNS | Lookup P50/P95、Failure Rate、Cache Hit |
| Connect/TLS | Connect P95、Handshake P95、Certificate Error |
| HTTP | Status分布、TTFB、Body Bytes、Pool Wait、Retry Count |
| SSE/WebSocket | Active Connections、Reconnect、Lag、Dropped Events |
| RPC | Method Latency、Status Code、Deadline Exceeded、Inflight |
| MCP | Server/Tool维度Success、JSON-RPC Error、Schema Failure |
| Tool | 业务成功率、Side Effect ID、依赖耗时、Result Size |
Trace负责解释单次请求,Metric负责发现群体异常,Log负责补充离散事件。若只记录模型Token和最终答案,Agent链路中的大部分关键诊断信息将不可见。
9. 超时、重试、幂等、背压与取消
选择协议只能解决通信能力,生产可靠性还取决于以下机制。
9.1 Deadline预算传播
设端到端剩余预算为,本层预留清理与序列化时间为,则下游Deadline至多为:
例如,上游只剩800 ms,本层预留100 ms,则下游调用最多分配700 ms。若每层都重新设置固定30秒Timeout,请求的实际生命周期可能远超上游预算。
9.2 暂态错误重试
适合有限重试的通常是连接重置、明确的Unavailable、限流或网关暂态失败;参数错误、权限拒绝和确定性业务冲突不应重试。重试需要:
- Exponential Backoff与Jitter;
- 最大次数和总Deadline;
- 幂等请求或Idempotency Key;
- Retry Budget,防止故障时请求风暴;
- 明确区分“没执行”“执行失败”和“执行结果未知”。
可以使用重试矩阵将重试策略定义为明确规则:
| 操作 | 错误 | 默认策略 |
|---|---|---|
| 只读、幂等 | Connect Timeout | 有预算时退避重试 |
| 只读、幂等 | HTTP 429 | 遵守Retry-After并加Jitter |
| 只读、幂等 | HTTP 500/503 | 有限重试,可配熔断 |
| 写操作、有幂等键 | 响应前断联 | 用同一Key重试或查询结果 |
| 写操作、无幂等键 | 响应前断联 | 结果未知,先查状态或人工处理 |
| 任意操作 | 400 / Invalid Params | 修请求,不重试 |
| 任意操作 | 401 / 403 | 重新认证或申请权限,不盲重试 |
| 任意操作 | Deadline耗尽 | 立即停止,不得在预算耗尽后继续执行 |
Hedged Request在首个请求变慢时向另一个副本发送第二个请求,以降低尾延迟。它仅适合幂等读,并需要全局额外流量预算;系统已经过载时,无约束的Hedge会进一步增加下游负载。
9.3 背压需要有界缓冲
Producer快于Consumer时,需要Bounded Queue、流控、降采样、合并进度事件或断开慢消费者。SSE进度可以丢弃可合并的token_delta,但不能丢失最终completed状态;RPC Stream应利用框架流控并限制单消息大小;MCP Tool Result过大时应返回Resource Link或对象存储URI,而不是将大量JSON直接写入模型上下文。
9.4 取消是跨层协作
客户端取消HTTP或RPC仅表示不再等待。服务端必须将Cancellation传播到数据库查询、下游HTTP、模型推理与Worker。对于已经产生外部副作用的工具,取消无法撤销既有结果,应进入补偿、查询最终状态或人工接管流程。
9.5 幂等键设计
一个可靠的写接口通常将幂等记录与业务写放在同一事务边界:
Idempotency-Key: inc-7:restart:payment:v3
key
request_hash
status: PROCESSING | SUCCEEDED | FAILED
response_code
response_hash / response_body_uri
side_effect_id
expires_at
相同Key但request_hash不同必须拒绝,避免调用方误复用Key。首个请求处于PROCESSING时,重复请求可以返回409/202 + status_uri;完成后返回已保存结果。TTL要覆盖客户端最大重试窗口和消息延迟。
数据库唯一约束防止重复插入,Idempotency Record防止重复业务执行,Outbox保证业务提交后事件最终可投递。三者位于不同层次,任何单一机制都不能独立保证端到端Exactly Once语义。
9.6 熔断、限流与隔离
- Timeout限制单次等待时间;
- Retry应对少量暂态失败;
- Circuit Breaker在依赖持续失败时快速拒绝,给它恢复空间;
- Rate Limit限制调用方速率;
- Bulkhead把不同Tool或租户隔离在独立并发池;
- Load Shedding在过载时优先拒绝低价值请求。
通常应先设置合理的Timeout和有界并发,再引入重试与熔断。熔断器无法替代线程池、队列和并发量的容量边界。
9.7 大型结果传输
模型上下文不是对象存储。Tool结果应设置:
- 最大JSON Body与单字段长度;
- 行数、时间范围和分页Cursor;
- PII与Secret脱敏;
content_hash与来源版本;- 大对象落对象存储,返回Resource Link或URI;
- Host按Token Budget选择摘要、片段或原文。
HTTP压缩减少网络字节,不减少解压后的内存和模型Token。一个10 MB gzip响应解压成200 MB JSON,照样能把进程请走。
10. 典型故障定位
10.1 SSE每60秒准时断一次
应优先检查Ingress、Load Balancer或Service Mesh的Idle Timeout,而不是将问题归因于随机TCP故障。检查项包括:
- 服务端是否真的定期写心跳;
- 写完是否Flush;
- 代理是否Buffer或压缩事件流;
- 每一跳Idle Timeout是多少;
- 重连是否携带
Last-Event-ID并完成补放。
若断开间隔非常稳定,先查配置计时器;若与网络切换相关,再查移动网络、NAT和客户端生命周期。
10.2 gRPC P99高但服务端Handler很快
检查Client连接池等待、DNS、负载均衡、HTTP/2并发Stream上限、Flow Control、序列化、大消息、Sidecar和下游Deadline。Handler Histogram只覆盖请求进入业务代码之后,排队和传输全被它无情开除。
10.3 MCP Tool偶发重复执行
先查Client或Gateway是否自动重试,再查请求是否携带稳定Idempotency Key。JSON-RPC id不能天然防重复:重连后Client可能生成新id,同一个id也可能在不同连接或生命周期里复用。去重必须使用业务幂等键或唯一约束。
10.4 HTTP 200但Agent仍报工具失败
依次看HTTP Body是否为JSON-RPC Error、Tool Result是否isError、outputSchema是否校验失败、Result是否被Host因大小或安全策略拒绝。HTTP层成功只说明拿到了合法HTTP响应。
10.5 同一个MCP Server本地正常、远程失败
stdio本地模式没有经历DNS、TLS、Gateway、OAuth、Origin校验和网络Body限制。远程失败要增加这些层的证据,不能拿本地tools/call成功证明公网部署没问题。
10.6 大量TIME_WAIT连接
首先检查是否存在短连接风暴、Client是否在每次请求时新建Transport、Keep-Alive是否被代理关闭,以及连接池参数是否错误。TIME_WAIT本身是正常状态;只有确认端口或连接资源已经成为瓶颈后,才应在评估风险的基础上调整系统参数。
11. 协议选型摘要
| 场景 | 优先选择 | 回答中的边界 |
|---|---|---|
| 浏览器看Agent长任务进度 | HTTP创建任务 + SSE看进度 | 事件持久化、Last-Event-ID、断线不取消任务 |
| 浏览器双向高频交互 | WebSocket | 自己补重连、Sequence、ACK和背压 |
| 内部强类型服务调用 | gRPC | Deadline、状态码、Proto兼容和负载均衡 |
| 公开资源型API | HTTP/JSON | Method语义、缓存、幂等和可观测性 |
| 本地Agent工具 | MCP over stdio | 子进程生命周期、stdout只写协议消息 |
| 共享远程Agent工具 | MCP over Streamable HTTP | 鉴权、Origin校验、版本、超时和无状态扩容 |
| 简单跨语言方法调用 | JSON-RPC或HTTP API | JSON-RPC只定义消息,不自动解决治理 |
| 极致定制二进制协议 | Raw Socket / 自定义Framing | 维护成本、安全、兼容性全由自己承担 |
12. 高频追问
HTTP是基于TCP吗
不能统一概括。HTTP/1.1和HTTP/2通常基于TCP,HTTP/3基于QUIC;HTTP语义已经与具体传输实现解耦。
SSE与长轮询有什么区别
长轮询是服务端有数据后结束一次响应,客户端再发下一次请求;SSE是一条持续响应,服务端连续追加事件。两者都需要处理代理超时、重连和消息游标。
SSE与WebSocket谁性能更高
问题缺少负载模型。单向低频通知更看重SSE的HTTP兼容和简单性;双向高频消息更适合WebSocket。连接数、消息大小、压缩、代理、TLS和背压策略通常比协议名字本身更决定性能。
RPC一定比REST快吗
不一定。gRPC的二进制序列化、HTTP/2复用和代码生成常有优势,但小流量下业务处理与数据库延迟可能占主导。REST是架构风格,RPC是调用抽象,二者也不是纯粹的性能对手。
MCP为什么用JSON-RPC而不是直接REST
MCP需要在同一逻辑协议中表达Request、Response、Notification、进度、取消和双向能力;JSON-RPC提供稳定的消息关联模型,而且不绑定HTTP资源路由。MCP再在其上补能力协商、工具、资源、Prompt、授权和传输约束。
MCP和API Gateway是什么关系
MCP定义AI能力交互语义,Gateway负责流量入口、认证、限流、路由、审计和策略执行。远程MCP Server可以部署在Gateway后面,但Gateway不会自动理解工具的业务风险,仍需Tool级权限和审批。
连接复用与会话是什么关系
连接是传输资源,会话是应用或协议状态。多个请求可以复用一条连接,一个会话也可能跨多条连接。把用户身份或任务状态只绑在连接上,会破坏负载均衡、断线恢复和水平扩容。
为什么TCP可靠,应用还会丢消息
TCP只保证一条连接内已确认字节的可靠、有序传输。进程可能在读取后、落库前崩溃;响应可能在业务提交后丢失;断线后新连接也不会自动补旧连接的应用消息。业务可靠性仍需要持久化、ACK、幂等和回放。
HTTP/2多路复用为什么还有队头阻塞
HTTP/2的Stream消除了HTTP/1.1应用层顺序响应的队头阻塞,但多个Stream仍共享一条TCP字节流。某个TCP Segment丢失时,后续字节要等待重传,其他Stream也会受影响。HTTP/3利用QUIC独立流缓解传输层队头阻塞。
Deadline和Timeout有什么区别
Timeout通常表示“最多等多久”,Deadline表示“不晚于哪个绝对时刻”。跨服务传播时使用Deadline更容易扣除已经消耗的时间;但要处理机器时钟误差,所以很多RPC框架在线路上换算成剩余Timeout。
MCP Server无状态以后,长任务放哪
无状态指协议请求可以由任意实例处理,不表示业务不能持久化。长任务应使用Task Handle,把状态放数据库、可靠队列或对象存储;任何实例都能根据Task ID查状态和取结果。
13. 分层总结
Socket为程序提供字节收发接口,TCP和QUIC负责端到端传输,HTTP定义Web消息语义,SSE与WebSocket提供不同交互模式,RPC将消息抽象为调用,JSON-RPC定义调用消息结构,MCP进一步定义Agent能力的发现与调用方式。按层分析可以明确各协议的职责、状态和故障边界;跨层混用概念会导致断线恢复、错误分类和重试语义无法准确说明。
参考规范
- RFC 9293:Transmission Control Protocol
- RFC 9110:HTTP Semantics
- RFC 9111:HTTP Caching
- WHATWG HTML:Server-Sent Events
- RFC 6455:The WebSocket Protocol
- gRPC Core Concepts
- Protocol Buffers:Proto3 Language Guide
- MCP 2025-06-18 Transports
- MCP 2025-06-18 Authorization
- MCP 2026-07-28 Specification Release
- MCP 2026-07-28 Tasks Extension说明