栏:AI 全栈开发|14
|-----------------------------------------------------------------------------------------------------------------------------|
| 前两篇已经分别把 SSE 和 WebSocket 学清楚。现在不再重复协议定义,而是回答真正会遇到的工程问题:一个 AI 产品到底应该在哪条链路用 SSE,在哪条链路用 WebSocket?判断依据不是"哪个更先进",而是你的通信形状和产品约束。 |
一、先看两个"都能做,但复杂度不同"的例子
场景 1:AI Chat
用户点一次发送,服务器开始持续返回模型输出。
|----------------------------------------------------------------------|
| Browser ── POST /chat ──▶ Server Browser ◀── Streaming/SSE ── Server |
客户端确实也有上行数据,但它不是"持续高频上行",而是一次明确的请求。
这种场景完全没必要因为"客户端也会发消息"就立刻上 WebSocket。
场景 2:在线协作编辑
|-------------------------------------------------------------------------|
| Browser A ◀════▶ Server Browser B ◀════▶ Server Browser C ◀════▶ Server |
光标、选区、编辑操作、在线状态都可能由双方随时发送。这里用一条长期双向通道会自然很多。
图 1 第一问是通信形状:一次请求 + 持续返回、单向持续推送,还是双方长期高频互发
二、第一条判断:客户端到底是不是"长期主动发送"
很多选型文章只说"单向选 SSE,双向选 WebSocket",这还不够精确。
因为 AI Chat 明明也存在双向通信:用户要把 prompt 发给服务器,用户还要点 Stop。
真正值得问的是:
客户端是否需要在同一条长期连接里,持续、低延迟、主动发送大量事件?
如果上行只是:
• 发一次 prompt。
• 偶尔取消任务。
• 修改一次配置。
普通 HTTP POST 已经很好。下行再用 SSE / Streaming 即可。
如果上行是:
• 持续输入位置。
• 实时控制指令。
• 多人协作操作。
• 游戏状态。
WebSocket 会更自然。
三、不要只看方向,还要看这 6 个维度
图 2 方向性只是第一维;请求方式、浏览器 API、重连、状态恢复和基础设施都会影响最终选型
|---------|---------------------------|------------------------------|
| 维度 | SSE / Streaming | WebSocket |
| 通信形状 | 服务器持续推送为主 | 双方长期主动收发 |
| 客户端上行 | 通常搭配普通 HTTP 请求 | 直接在同一连接发消息 |
| 浏览器 API | EventSource 或 fetch | WebSocket |
| 重连 | EventSource 可自动尝试重连 | 浏览器端需要自己实现 |
| 断点恢复 | 可利用 id / Last-Event-ID 设计 | 自己设计 seq / replay / snapshot |
| 消息类型 | SSE 是 UTF-8 文本事件 | Text / Binary Message |
| 生命周期 | HTTP 流连接 | 独立长期连接状态 |
四、EventSource 不是 SSE 的全部,SSE 也不只适合 GET
如果 AI Chat 需要:
|---------------------------------------------|
| POST /chat { "message": "解释一下 Agent Loop" } |
然后同一个 HTTP 响应持续返回 SSE 事件,可以直接用 fetch() 读取 text/event-stream。
所以:
• SSE 是事件格式。
• EventSource 是消费 SSE 的一种浏览器 API。
• POST + fetch SSE 同样是常见 AI Chat 方案。
这点会直接影响选型:不要因为 EventSource 只能 GET,就误判"聊天必须 WebSocket"。
五、WebSocket 也不是因为"全双工"就一定更快
建立 WebSocket 以后,后续消息确实不需要为每条业务消息重新建立 HTTP 请求语义。
但用户真正感知的延迟还取决于:
• 网络 RTT。
• 后端排队和处理时间。
• 模型推理耗时。
• 代理和服务端实现。
对于 LLM 生成这种动辄几百毫秒甚至数秒的任务,HTTP / SSE 的额外开销往往不是主要瓶颈。
所以不能简单写成"低延迟就一定 WebSocket"。
六、重连:SSE 确实省事,但不是"可靠消息自动完成"
EventSource 的自动重连很方便;WebSocket 浏览器端需要自己写重连。
但二者断线以后都要问:
断线期间发生的数据怎么办?
SSE 可以配合:
|--------------------------------------------|
| id: 120 data: ... # 重连时浏览器提供 Last-Event-ID |
但前提是服务端保存了事件历史,并且知道怎样从 121 开始重放。
WebSocket 则通常自己定义:
|--------------------------------------------|
| last_seq = 120 reconnect() sync(after=120) |
所以"自动重连"和"状态恢复"是两件不同的事。
七、认证:两边都能做,但方式不同
SSE / EventSource
EventSource 不能像 fetch 一样任意加 Authorization Header。常见方案包括同源 Cookie、withCredentials,或者先 POST 创建任务后拿一个不可预测的短期订阅 ID。
fetch SSE
如果用 fetch 消费 SSE,就可以正常使用 POST、Authorization Header 等 fetch 能力。
WebSocket
浏览器 new WebSocket() 同样不能随意设置自定义 Authorization Header。常见做法包括 Cookie、短期 ticket、Subprotocol 等设计。
|-------------------------------------------------------------------------|
| 因此不要写成"WebSocket 鉴权更灵活,因为握手可以随便带 Header"。在浏览器 API 下,两者都有约束,应该从实际认证架构设计。 |
八、代理与基础设施:SSE 不等于"完全白嫖 HTTP 基建"
SSE 建立在 HTTP 上,通常更容易接入现有 Web 基础设施,这是优势。
但仍然可能遇到:
• 代理缓冲导致不流式。
• CDN / LB idle timeout。
• 长响应超时配置。
• 压缩或中间件缓冲。
WebSocket 则需要中间层支持连接升级或对应的现代 WebSocket 建连方式,并管理长期连接。
但现代 Nginx、云负载均衡和网关对 WebSocket 支持已经很成熟,不能简单写成"WebSocket 很难穿代理"。
九、连接数和资源:不要简单说"WebSocket 比 SSE 贵很多"
两者都是长时间保持连接。
每个在线客户端都可能占用:
• 连接状态。
• 文件描述符 / socket 资源。
• 服务端内存和缓冲区。
• 心跳或超时管理。
具体哪个更贵,取决于实现、HTTP 版本、网关、消息频率和业务状态,不能仅凭协议名字下结论。
真正的容量规划要压测。
十、多实例部署真的必须 Sticky Session 吗
WebSocket 一条已经建立的连接当然会落在某个具体实例上。
但这不等于"整个 WebSocket 系统必须 sticky session"。
如果用户重连后落到另一个实例,只要:
• 认证状态可共享。
• 房间 / 订阅状态可以恢复。
• 跨实例消息通过 Redis、Kafka、NATS 等机制分发。
完全可以正常工作。
Sticky Session 是一种部署策略,不是 WebSocket 协议要求。
SSE 同样可能需要共享事件源,否则重连到另一个实例也无法续传。
十一、一个 AI 产品里,三种通道完全可以同时存在
图 3 按业务链路选协议:普通 REST、Streaming/SSE、WebSocket 可以在同一个产品里并存
例如一个完整 AI 工作台:
|-----------|------------------------|
| 业务链路 | 推荐方式 |
| 发送 Prompt | POST /chat |
| 模型增量输出 | fetch Streaming / SSE |
| 停止生成 | POST /runs/{id}/cancel |
| 后台任务进度 | SSE |
| 多人协作编辑 | WebSocket |
| 普通 CRUD | REST API |
这个设计反而比"全站统一 WebSocket"或者"所有实时都 SSE"更容易理解和维护。
十二、快速选型:按问题问,而不是按协议背
|-------------------|-----------------|-----------------------------|
| 先问什么 | 如果答案是...... | 更值得先考虑 |
| 服务器推为主吗? | 是 | SSE / Streaming |
| 客户端需要长期高频主动发吗? | 是 | WebSocket |
| 只有一次 POST + 长输出吗? | 是 | fetch Streaming / SSE |
| 需要浏览器自动重连吗? | 很重要 | EventSource SSE 有优势 |
| 需要二进制双向消息吗? | 是 | WebSocket |
| 断线必须无缝恢复吗? | 是 | 两者都要额外设计事件历史 / seq / replay |
十三、几个最容易背错的结论
• 误区 1:单向一定 SSE、双向一定 WebSocket。偶尔上行 POST + 持续下行仍然很适合 SSE。
• 误区 2:AI Chat 教科书级必须 SSE。fetch Streaming 也很常见;具体选事件协议还是裸流取决于前端事件需求。
• 误区 3:WebSocket 天生比 SSE 更低延迟。真实延迟由整条链路决定。
• 误区 4:SSE 自动重连就自动断点续传。服务端仍要保存和重放事件。
• 误区 5:WebSocket 一定需要 Sticky Session。Sticky 只是多实例架构中的一个可选策略。
• 误区 6:股票行情 / 在线游戏都只能 WebSocket。行情如果只是单向推送也可能使用 SSE;游戏、音视频还可能需要更专门的实时协议。
十四、下一篇
下一篇 15《同步、异步、并发到底有什么区别?》会离开具体传输协议,开始看 FastAPI 后端真正的运行模型:为什么等待模型 API 时 async 很有价值,为什么 CPU 密集任务不能因为加了 async def 就自动变快。