副标题:从 MCP 协议演进看实时通信技术的选择
在现代 Web 实时通信领域,SSE (Server-Sent Events) 和 Streamable-HTTP 是两种重要的技术方案。尤其在 Anthropic 的 MCP (Model Context Protocol) 协议从 SSE 迁移到 Streamable-HTTP 的背景下,理解这两种技术的差异变得尤为重要。
SSE (Server-Sent Events) 是 HTML5 规范的一部分,是一种基于 HTTP 的单向服务器推送技术。
┌─────────────┐ ┌─────────────┐
│ 客户端 │ ←─────数据流────── │ 服务器 │
│ EventSource │ │ HTTP SSE │
└─────────────┘ └─────────────┘
↑ │
└──────────── 不发送 ──────────────┘
| 特性 | 说明 |
|---|---|
| 协议 | 基于 HTTP/1.1 或 HTTP/2 |
| 方向 | 单向(服务器 → 客户端) |
| Content-Type | text/event-stream |
| 重连机制 | 浏览器原生自动重连 |
| 事件类型 | 支持命名事件(event: name) |
| 浏览器支持 | 现代浏览器原生支持(IE 除外) |
data: 这是消息内容\n\n
event: custom-event\ndata: {"key": "value"}\n\n
id: 123\nevent: update\ndata: 新消息\n\n
retry: 5000\n\n
// 原生 EventSource API
const source = new EventSource('/sse/events');
// 监听默认消息
source.onmessage = (event) => {
console.log('收到:', event.data);
};
// 监听命名事件
source.addEventListener('custom-event', (event) => {
console.log('自定义事件:', event.data);
});
// 错误处理
source.onerror = (error) => {
console.error('SSE 错误:', error);
};
Streamable-HTTP 是指使用标准 HTTP 协议进行流式数据传输的机制,通常配合 Transfer-Encoding: chunked 实现。
┌─────────────┐ POST 请求 ┌─────────────┐
│ 客户端 │ ─────────────────→ │ 服务器 │
│ │ ←─────数据块 1────── │ │
│ │ ←─────数据块 2────── │ │
│ fetch() │ ←─────数据块 N────── │ HTTP 流 │
└─────────────┘ └─────────────┘
↑ │
└────────── GET 请求(可选)─────────┘
| 特性 | 说明 |
|---|---|
| 协议 | 标准 HTTP(POST/GET) |
| 方向 | 双向(通过不同请求) |
| Content-Type | 任意(通常是 application/json) |
| 重连机制 | 需手动实现 |
| 事件类型 | 需自定义协议 |
| 浏览器支持 | 所有支持 fetch/xhr 的浏览器 |
// 使用 fetch API
const response = await fetch('/api/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify({ query: 'hello' })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
console.log('收到块:', chunk);
}
| 对比维度 | SSE | Streamable-HTTP |
|---|---|---|
| 协议基础 | HTTP | HTTP |
| Content-Type | text/event-stream | 任意(application/json 等) |
| 通信方向 | 单向(服务器→客户端) | 双向(通过 POST+响应流) |
| 客户端 API | EventSource(原生) | fetch + ReadableStream |
| 自动重连 | ✅ 浏览器原生支持 | ❌ 需手动实现 |
| 事件类型 | ✅ 原生支持命名事件 | ❌ 需自定义格式 |
| 消息 ID | ✅ 原生支持 | ❌ 需自定义 |
| 自定义头 | ❌ EventSource 不支持 | ✅ fetch 支持任意头 |
| POST 请求 | ❌ 只能 GET | ✅ 支持所有 HTTP 方法 |
| 认证方式 | 受限(URL 参数/Cookie) | 完整(Bearer Token 等) |
| 跨域处理 | 需单独配置 | 标准 CORS |
| 二进制数据 | ❌ 仅文本(Base64 编码) | ✅ 支持 |
| 连接数限制 | HTTP/1.1: 6个/域 | HTTP/2: 无限制 |
| 服务器资源 | 高(长连接) | 低(可短暂连接) |
| 复杂度 | 低 | 中 |
┌─────────────────────────────────────────────┐
│ SSE 模式 │
├─────────────────────────────────────────────┤
│ │
│ 客户端 服务器 │
│ │ │ │
│ │── GET /sse ───────→│ │
│ │ │ │
│ │←─ data: hello ─────│ │
│ │←─ data: world ─────│ │
│ │←─ ... │ │
│ │ │ │
│ │── POST /send ─────→│ (需额外端点) │
│ │ │ │
│ [长连接保持] [维护连接状态] │
│ │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Streamable-HTTP 模式 │
├─────────────────────────────────────────────┤
│ │
│ 客户端 服务器 │
│ │ │ │
│ │── POST /mcp ──────→│ │
│ │ {json-rpc} │ │
│ │ │ │
│ │←─ 响应块 1 ─────────│ │
│ │←─ 响应块 2 ─────────│ │
│ │←─ ... │ │
│ │ │ │
│ [状态可选] [可状态可无状态] │
│ │
└─────────────────────────────────────────────┘
MCP SSE 模式:
1. 建立 SSE 连接
GET /sse ──────────────────────────→
←──────────── endpoint: /msg/123 ─
2. 发送消息(需额外 POST 端点)
POST /msg/123 ─────────────────────→
←──────────── 响应 ──────────────
3. 接收流式响应
←──────────── data: {...} ───────
←──────────── data: {...} ───────
问题:
- 需要两个端点(SSE + POST)
- 必须保持长连接
- 高可用部署复杂
MCP Streamable-HTTP 模式:
单一端点支持双向通信:
POST /mcp ───────────────────────────→
{json-rpc 请求} │
│
←─────── 流式响应块 1 ───────────│
←─────── 流式响应块 2 ───────────│
←─────── ... ───────────────────│
优势:
- 单一端点
- 支持无状态服务器
- 可选择性使用 SSE 进行流式传输
- 更好的基础设施兼容性
| 问题 | SSE 方案 | Streamable-HTTP 方案 |
|---|---|---|
| 长连接维护 | 必须保持 | 可选短暂连接 |
| 高可用 | 需要连接持久化 | 无状态即可 |
| 扩展性 | 连接数受限 | 更好的水平扩展 |
| 认证 | 受限于 EventSource | 完整的 HTTP 认证 |
| 双向通信 | 需要额外端点 | 单一端点完成 |
✅ 实时通知推送
✅ 需要完整 HTTP 控制
// 客户端
const source = new EventSource('/chat?query=hello');
let response = '';
source.onmessage = (event) => {
if (event.data === '[DONE]') {
source.close();
return;
}
const data = JSON.parse(event.data);
response += data.content;
updateUI(response);
};
// 服务端 (Node.js)
app.get('/chat', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
const query = req.query.query;
const stream = getAIStream(query);
stream.on('data', (chunk) => {
res.write(`data: ${JSON.stringify({content: chunk})}\n\n`);
});
stream.on('end', () => {
res.write('data: [DONE]\n\n');
res.end();
});
});
// 客户端
const response = await fetch('/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
query: 'hello',
context: [...] // 可以发送复杂数据
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let response = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunks = decoder.decode(value).split('\n');
for (const chunk of chunks) {
if (chunk.trim()) {
const data = JSON.parse(chunk);
response += data.content;
updateUI(response);
}
}
}
// 服务端 (Node.js)
app.post('/chat', async (req, res) => {
const { query, context } = req.body; // 可以接收复杂数据
res.setHeader('Content-Type', 'application/json');
res.setHeader('Transfer-Encoding', 'chunked');
const stream = getAIStream(query, context);
stream.on('data', (chunk) => {
res.write(JSON.stringify({content: chunk}) + '\n');
});
stream.on('end', () => {
res.end();
});
});
| 指标 | SSE | Streamable-HTTP |
|---|---|---|
| 首次连接延迟 | ~50-100ms | ~50-100ms |
| 消息延迟 | ~10-50ms | ~10-50ms |
| 内存占用(服务器) | 高(维持连接) | 低(短暂连接) |
| 并发连接数 | 受限(HTTP/1.1) | 更高(HTTP/2) |
| 断线恢复 | 自动(带 ID) | 需手动实现 |
| 网络开销 | 低(长连接) | 中(按需连接) |
┌─────────────────────────────────────────────────────┐
│ 选择建议 │
├─────────────────────────────────────────────────────┤
│ │
│ 简单单向推送 + 浏览器优先 + 自动重连需求 │
│ ↓ │
│ ┌─────────────┐ │
│ │ SSE │ │
│ └─────────────┘ │
│ │
│ 需要认证 + 双向通信 + 无状态部署 + AI 应用 │
│ ↓ │
│ ┌─────────────────────┐ │
│ │ Streamable-HTTP │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘
| SSE | Streamable-HTTP |
|---|---|
| 简单即插即用 | 更灵活但复杂 |
| 浏览器原生支持 | 需处理流解析 |
| 单向通信最佳 | 双向通信最佳 |
| 实时通知首选 | AI 应用首选 |
| 长连接模式 | 无状态友好 |
研究时间:2026-03-07
标签: #SSE #StreamableHTTP #MCP #实时通信 #HTTP #Web开发 #小凯
还没有人回复