SSE 和 Streamable HTTP

1. 概述

SSEStreamable HTTP 都与 基于 HTTP 的流式传输 有关,但它们不是同一层面的概念。

  • SSE:是一种 具体的浏览器/HTTP 推送技术标准 ,全称 Server-Sent Events
  • Streamable HTTP:通常指 可流式返回数据的 HTTP 交互方式 ,更偏向一种能力描述或设计模式,而不是像 SSE 那样严格、单一的浏览器标准。

如果放在实际工程语境里,可以这样理解:

  • SSE 是一种"如何流"的 标准方案
  • Streamable HTTP 是"HTTP 请求/响应可以边生成边返回"的 更宽泛能力 ,其中可以用 SSE,也可以不用 SSE。

2. 什么是 SSE

2.1 定义

SSE 的全称是 Server-Sent Events ,即 服务器向客户端单向持续推送事件 的机制。它基于 HTTP,最常见的响应头是:

复制代码

服务器建立一个 HTTP 连接后,不立即一次性返回完整响应,而是 持续不断地往连接里写入事件数据 ;客户端一边接收,一边处理。

它最典型的使用方式是在浏览器中通过 EventSource API 订阅服务端事件流。

2.2 工作方式

SSE 的典型流程:

  1. 客户端向服务端发起一个普通 HTTP 请求。
  2. 服务端返回 text/event-stream
  3. 连接保持打开。
  4. 服务端按约定格式持续写入事件。
  5. 客户端收到一条处理一条。
  6. 若连接断开,浏览器通常会自动重连。

2.3 数据格式

SSE 的消息是 文本协议 ,常见字段包括:

  • event: 事件名
  • data: 事件内容
  • id: 事件 ID
  • retry: 重连等待时间

示例:

复制代码

注意:

  • 每个事件以一个空行结束。
  • 一条事件里可以有多行 data:
  • 默认事件类型可不写 event:

2.4 浏览器端示例

复制代码

2.5 适用场景

SSE 很适合:

  • 实时通知
  • 任务进度更新
  • 日志流展示
  • AI 文本生成逐字/逐段返回
  • 股票/监控面板的轻量实时刷新

2.6 优点

  • 基于 HTTP,部署和接入成本低
  • 浏览器原生支持 EventSource
  • 自动重连能力较方便
  • 服务端到客户端的单向推送足够简单直接
  • 文本流天然适合展示增量内容

2.7 局限

  • 只能服务端推客户端,不能双向实时通信
  • 主要是文本格式,不适合复杂二进制通信
  • 某些代理、网关、超时配置可能影响长连接稳定性
  • 浏览器端的请求控制能力不如 fetch 灵活
  • 不适合强交互双向会话场景,那类场景更常用 WebSocket 或 WebRTC

3. 什么是 Streamable HTTP

3.1 定义

Streamable HTTP 不是一个像 SSE 那样明确统一、单一的浏览器 API 名称。它通常表示:

HTTP 响应内容不是一次性整体返回,而是可以边生成边发送,客户端边接收边处理。

也就是说,它强调的是 HTTP 的流式传输能力

在不同上下文里,Streamable HTTP 可能指:

  • 基于 HTTP chunked transfer 的流式响应
  • 基于 fetch + ReadableStream 的增量读取
  • 用 SSE 作为流格式封装
  • 用 NDJSON、纯文本分块、分隔符协议等方式做流式输出
  • 某些协议框架里定义的一类"可通过 HTTP 流式承载消息"的传输方式

3.2 核心思想

普通 HTTP 常见模式是:

  • 服务端准备好完整结果
  • 一次性返回
  • 客户端一次性读取

而 Streamable HTTP 是:

  • 服务端先返回响应头
  • 后续内容分块持续写出
  • 客户端按块读取并处理

这类能力在 AI、日志、长任务、推理过程展示中非常常见。

3.3 实现方式

它的底层通常依赖:

  • HTTP/1.1 的 Transfer-Encoding: chunked
  • HTTP/2/HTTP/3 的流式帧传输能力
  • 客户端的流读取接口,如 ReadableStream

前端示例:

复制代码

这里并没有使用 SSE,但它同样是 streamable HTTP response

3.4 常见流格式

Streamable HTTP 不限定必须使用哪种消息格式,常见包括:

  • text/event-stream:也就是 SSE
  • application/x-ndjson:每行一个 JSON
  • text/plain:纯文本增量输出
  • application/json-seq:序列化 JSON 流
  • 自定义分隔符协议

所以它比 SSE 更宽泛。

3.5 适用场景

  • 大模型输出 token 流
  • 服务端长任务的分阶段结果回传
  • 实时日志/审计流
  • 数据导出进度与增量结果展示
  • 后端代理上游流式接口并继续往前端透传

3.6 优点

  • 保持 HTTP 语义,易接入现有网关、鉴权、观测体系
  • 比 WebSocket 更容易融入传统 API 架构
  • 不限定消息格式,灵活性更高
  • 对 AI 输出、日志、长任务尤其合适

3.7 局限

  • 标准化程度不如 SSE/ WebSocket 明确
  • 不同实现之间协议格式可能不统一
  • 如果需要客户端主动高频回传,仍然不如双向协议自然
  • 容易受反向代理缓冲、超时、压缩策略影响

4. SSE 与 Streamable HTTP 的关系

4.1 包含关系

可以把两者关系理解为:

  • Streamable HTTP大类
  • SSE 是这个大类下的一种 具体协议形式

即:

复制代码

也就是:

  • 所有 SSE 都属于一种 HTTP 流式传输
  • 但并不是所有 Streamable HTTP 都是 SSE

4.2 一个直观类比

可以类比成:

  • Streamable HTTP 像"流式运输"这类总方法
  • SSE 像其中一种固定规格的运输箱

你可以用标准运输箱,也可以用别的包装方式;只要是边发边收,都可以叫 streamable。

5. 两者的关键区别

表格

对比项 SSE Streamable HTTP

|-----------------|----------------------|--------------------------------|
| 性质 | 具体标准/协议格式 | 泛化能力/设计方式 |
| 典型 Content-Type | text/event-stream | 不固定 |
| 浏览器支持 | 有 EventSource 原生支持 | 通常用 fetch + ReadableStream |
| 消息格式 | 有固定字段规范 | 可自定义 |
| 通信方向 | 单向:服务端到客户端 | 通常也是服务端流式返回,但表达更宽泛 |
| 适合场景 | 通知、进度、事件流 | AI 输出、日志流、任意增量结果 |
| 标准化程度 | 较高 | 较低,依实现而定 |
| 灵活性 | 中等 | 高 |

6. 为什么 AI 场景里经常同时提到这两个词

在大模型或 Agent 场景中,常常会看到:

  • 某个接口支持 streaming
  • 返回方式可能是 SSE
  • 或者文档中写成 streamable HTTP

原因是:

  1. 业务需求天然适合流式返回

    大模型生成文本是逐步完成的,不必等全部生成后再返回。

  2. HTTP 基础设施成熟

    企业现有鉴权、网关、限流、日志系统普遍围绕 HTTP 构建。

  3. SSE 很适合"服务端不断吐文本"

    例如 token-by-token 输出、步骤更新、工具调用状态通知。

  4. 但有时又不想被 SSE 格式限制

    比如希望直接传 NDJSON、自定义 JSON chunk,或者兼容更复杂客户端。

因此很多系统会:

  • 对浏览器前端提供 SSE
  • 对服务间调用提供自定义 streamable HTTP 格式
  • 或者同一接口兼容多种模式

7. 与 WebSocket 的区别

用户在理解 SSE / Streamable HTTP 时,经常会与 WebSocket 混淆。

7.1 SSE / Streamable HTTP

  • 基于 HTTP 语义
  • 更偏"请求后持续返回结果"
  • 服务端向客户端输出最自然
  • 易复用现有 HTTP 基础设施

7.2 WebSocket

  • 建立后变成真正的双向长连接
  • 客户端和服务端都可以随时主动发消息
  • 更适合聊天室、协同编辑、在线游戏、低延迟双向交互

7.3 如何选

如果需求是:

  • 用户发起一个请求
  • 服务端持续返回进度/文本/事件

优先考虑:

  • SSE
  • 或一般的 Streamable HTTP

如果需求是:

  • 双方都要随时发消息
  • 高频交互
  • 真正的实时双向状态同步

优先考虑:

  • WebSocket

8. 实际工程中的注意点

8.1 代理和网关缓冲

很多 Nginx、网关、CDN 默认会缓冲响应,这会让"流式"看起来失效。常见问题是:

  • 服务端明明在持续写
  • 客户端却最后一次性收到

这通常不是代码逻辑问题,而是链路上的缓冲策略导致。

8.2 超时设置

流式请求往往持续时间更长,需要关注:

  • upstream timeout
  • idle timeout
  • read timeout
  • 客户端超时

8.3 心跳机制

长连接流式场景下,经常需要定期发送心跳,避免链路被中间层误判为空闲连接。

在 SSE 中,经常用注释行或空事件实现保活。

8.4 消息边界设计

如果不用 SSE,而是自定义 Streamable HTTP,必须明确:

  • 一条消息如何结束
  • 客户端如何切包
  • 错误如何表达
  • 结束标记是什么

否则客户端很难稳定解析。

8.5 重连与幂等

SSE 对自动重连支持更自然;如果是普通 streamable HTTP,自定义协议时需要自行考虑:

  • 断线后是否支持续传
  • 是否需要 message id
  • 重放会不会重复消费

9. 一个简明结论

如果只用一句话概括:

  • SSE 是一种标准化的、基于 HTTP 的服务端单向事件流协议。
  • Streamable HTTP 是更宽泛的概念,指 HTTP 响应可以被持续分块发送和消费,不限定必须采用 SSE 格式。

两者关系不是并列竞争,而更像:

  • SSE 是一种具体实现
  • Streamable HTTP 是更上层的能力描述

10. 选型建议

10.1 适合选 SSE 的情况

  • 浏览器前端直接消费
  • 需要简单稳定的服务端推送
  • 事件/进度/文本流为主
  • 不需要复杂双向通信

10.2 适合选一般 Streamable HTTP 的情况

  • 希望保留 HTTP 流式能力,但不受 SSE 格式限制
  • 客户端并不一定是浏览器 EventSource
  • 需要传 NDJSON、自定义 JSON chunk 或其他协议
  • 主要是 AI 推理、日志、批量结果增量返回

10.3 适合选 WebSocket 的情况

  • 强双向实时交互
  • 客户端也要频繁主动推送消息
  • 会话型实时系统