NDJSON、SSE 与流式协议边界
对比 NDJSON 与 SSE 的记录边界、元数据、客户端能力和代理行为,并说明流读取与事件解析的分层。
#type / synthesis
#status / evergreen
#tech / dev / frontend
#resource / javascript
#protocol / http
[!info] related notes
- 字节读取:Web Streams 与增量文本解码
- 取消:AbortController 与异步取消
- 前端实践:前端消费 SSE
NDJSON、SSE 与流式协议边界
共同目标
NDJSON 和 SSE 都允许服务端在一个 HTTP 响应中逐条发送记录,但它们定义记录边界的方式不同。
| 维度 | NDJSON | SSE |
|---|---|---|
| 常见媒体类型 | application/x-ndjson | text/event-stream |
| 记录单位 | 每行一个 JSON 值 | 空行分隔事件,内部有 event:、data: 等字段 |
| 元数据 | 放进 JSON 对象 | 协议自带 event/id/retry 字段 |
| 原生浏览器 API | 通常用 fetch + Streams | 可用 EventSource,也可用 fetch |
| 请求方式 | fetch 可使用任意方法和请求体 | EventSource 主要面向 GET |
正确的解析分层
HTTP body 字节块
-> TextDecoder 还原字符
-> 协议解析器识别完整行/事件
-> JSON.parse 得到 unknown
-> 运行时契约校验
-> 业务事件 reducer
每层只解决一种边界。JSON.parse 不应该直接处理任意网络块,业务 reducer 也不应该负责拼接半行。
SSE 要点
- 一个事件可包含多个
data:行。 - 空行表示事件结束。
:开头是注释,可用于 keep-alive。id:可帮助断线恢复,但客户端和服务端必须共同定义回放策略。
NDJSON 要点
- 每一行必须是独立合法 JSON。
- JSON 字符串内部的换行应被转义,不能成为实际记录分隔符。
- 最后一行是否必须以换行结尾需要在协议中明确。
工程选择
- 需要 POST 请求体、结构简单、双端都自己控制:NDJSON 很直接。
- 需要浏览器原生重连语义或标准事件字段:SSE 更合适。
- 无论选择哪种,都要处理代理缓冲、心跳、取消、错误事件和断线后的重复/缺失。