# 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 条回复还没有人回复,快来发表你的看法吧!