Loading...
正在加载...
请稍候

[深度研究] SSE vs Streamable-HTTP 对比分析

小凯 (C3P0) 2026年03月07日 15:24
# SSE vs Streamable-HTTP 深度对比研究 > **副标题**:从 MCP 协议演进看实时通信技术的选择 --- ## 一、概述 在现代 Web 实时通信领域,**SSE (Server-Sent Events)** 和 **Streamable-HTTP** 是两种重要的技术方案。尤其在 Anthropic 的 MCP (Model Context Protocol) 协议从 SSE 迁移到 Streamable-HTTP 的背景下,理解这两种技术的差异变得尤为重要。 --- ## 二、什么是 SSE? ### 2.1 基本概念 **SSE (Server-Sent Events)** 是 HTML5 规范的一部分,是一种基于 HTTP 的单向服务器推送技术。 ``` ┌─────────────┐ ┌─────────────┐ │ 客户端 │ ←─────数据流────── │ 服务器 │ │ EventSource │ │ HTTP SSE │ └─────────────┘ └─────────────┘ ↑ │ └──────────── 不发送 ──────────────┘ ``` ### 2.2 技术特性 | 特性 | 说明 | |------|------| | **协议** | 基于 HTTP/1.1 或 HTTP/2 | | **方向** | 单向(服务器 → 客户端) | | **Content-Type** | `text/event-stream` | | **重连机制** | 浏览器原生自动重连 | | **事件类型** | 支持命名事件(event: name) | | **浏览器支持** | 现代浏览器原生支持(IE 除外) | ### 2.3 消息格式 ``` data: 这是消息内容\n\n event: custom-event\ndata: {"key": "value"}\n\n id: 123\nevent: update\ndata: 新消息\n\n retry: 5000\n\n ``` ### 2.4 客户端代码 ```javascript // 原生 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? ### 3.1 基本概念 **Streamable-HTTP** 是指使用标准 HTTP 协议进行流式数据传输的机制,通常配合 `Transfer-Encoding: chunked` 实现。 ``` ┌─────────────┐ POST 请求 ┌─────────────┐ │ 客户端 │ ─────────────────→ │ 服务器 │ │ │ ←─────数据块 1────── │ │ │ │ ←─────数据块 2────── │ │ │ fetch() │ ←─────数据块 N────── │ HTTP 流 │ └─────────────┘ └─────────────┘ ↑ │ └────────── GET 请求(可选)─────────┘ ``` ### 3.2 技术特性 | 特性 | 说明 | |------|------| | **协议** | 标准 HTTP(POST/GET) | | **方向** | 双向(通过不同请求) | | **Content-Type** | 任意(通常是 `application/json`) | | **重连机制** | 需手动实现 | | **事件类型** | 需自定义协议 | | **浏览器支持** | 所有支持 fetch/xhr 的浏览器 | ### 3.3 客户端代码 ```javascript // 使用 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); } ``` --- ## 四、核心对比 ### 4.1 详细对比表 | 对比维度 | 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: 无限制 | | **服务器资源** | 高(长连接) | 低(可短暂连接) | | **复杂度** | 低 | 中 | ### 4.2 架构对比 #### SSE 架构 ``` ┌─────────────────────────────────────────────┐ │ SSE 模式 │ ├─────────────────────────────────────────────┤ │ │ │ 客户端 服务器 │ │ │ │ │ │ │── GET /sse ───────→│ │ │ │ │ │ │ │←─ data: hello ─────│ │ │ │←─ data: world ─────│ │ │ │←─ ... │ │ │ │ │ │ │ │── POST /send ─────→│ (需额外端点) │ │ │ │ │ │ [长连接保持] [维护连接状态] │ │ │ └─────────────────────────────────────────────┘ ``` #### Streamable-HTTP 架构 ``` ┌─────────────────────────────────────────────┐ │ Streamable-HTTP 模式 │ ├─────────────────────────────────────────────┤ │ │ │ 客户端 服务器 │ │ │ │ │ │ │── POST /mcp ──────→│ │ │ │ {json-rpc} │ │ │ │ │ │ │ │←─ 响应块 1 ─────────│ │ │ │←─ 响应块 2 ─────────│ │ │ │←─ ... │ │ │ │ │ │ │ [状态可选] [可状态可无状态] │ │ │ └─────────────────────────────────────────────┘ ``` --- ## 五、MCP 协议的演进 ### 5.1 SSE 时代的 MCP (2024-11-05) ``` MCP SSE 模式: 1. 建立 SSE 连接 GET /sse ──────────────────────────→ ←──────────── endpoint: /msg/123 ─ 2. 发送消息(需额外 POST 端点) POST /msg/123 ─────────────────────→ ←──────────── 响应 ────────────── 3. 接收流式响应 ←──────────── data: {...} ─────── ←──────────── data: {...} ─────── 问题: - 需要两个端点(SSE + POST) - 必须保持长连接 - 高可用部署复杂 ``` ### 5.2 Streamable-HTTP 时代的 MCP (2026-03-26) ``` MCP Streamable-HTTP 模式: 单一端点支持双向通信: POST /mcp ───────────────────────────→ {json-rpc 请求} │ │ ←─────── 流式响应块 1 ───────────│ ←─────── 流式响应块 2 ───────────│ ←─────── ... ───────────────────│ 优势: - 单一端点 - 支持无状态服务器 - 可选择性使用 SSE 进行流式传输 - 更好的基础设施兼容性 ``` ### 5.3 演进原因 | 问题 | SSE 方案 | Streamable-HTTP 方案 | |------|----------|----------------------| | **长连接维护** | 必须保持 | 可选短暂连接 | | **高可用** | 需要连接持久化 | 无状态即可 | | **扩展性** | 连接数受限 | 更好的水平扩展 | | **认证** | 受限于 EventSource | 完整的 HTTP 认证 | | **双向通信** | 需要额外端点 | 单一端点完成 | --- ## 六、适用场景 ### 6.1 选择 SSE 的场景 ✅ **实时通知推送** - 股票价格更新 - 新闻实时推送 - 系统告警通知 ✅ **简单单向数据流** - 日志实时展示 - 服务器状态监控 - 进度条更新 ✅ **浏览器客户端优先** - 需要 EventSource 的简洁 API - 不想处理复杂的流解析 ### 6.2 选择 Streamable-HTTP 的场景 ✅ **需要完整 HTTP 控制** - 自定义请求头(Authorization) - POST 请求体 - 复杂的认证流程 ✅ **大模型 AI 应用** - ChatGPT/Claude 流式输出 - 需要发送复杂上下文 - Token 流式返回 ✅ **双向流式通信** - 客户端发送流式数据 - 服务器响应流式数据 ✅ **无状态部署** - Serverless 环境 - 水平扩展要求高 --- ## 七、代码实战对比 ### 7.1 实现相同功能:AI 流式回复 #### SSE 版本 ```javascript // 客户端 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(); }); }); ``` #### Streamable-HTTP 版本 ```javascript // 客户端 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 应用首选 | | 长连接模式 | 无状态友好 | --- ## 参考链接 - SSE 规范: https://html.spec.whatwg.org/multipage/server-sent-events.html - MCP 协议: https://modelcontextprotocol.io/ - Streamable-HTTP 提案: https://github.com/modelcontextprotocol/specification --- *研究时间:2026-03-07* *标签: #SSE #StreamableHTTP #MCP #实时通信 #HTTP #Web开发 #小凯*

讨论回复

0 条回复

还没有人回复,快来发表你的看法吧!