SSE 还是 WebSocket?大模型流式接口的前端选型
对接过两种方案之后,聊聊大模型对话场景下 SSE 与 WebSocket 各自的脾气。
做 AI 安全平台时对接过 SSE 的推理接口,做地震应急系统时维护过 WebSocket 的实时预警通道。两种”流”都伺候过之后,经常被问:大模型对话到底该用哪个?
先给结论:对话类场景默认 SSE,需要双向控制或多会话并发时才考虑 WebSocket。下面说为什么。
先看数据流的形状
大模型对话的通信模式是”一问一大串”:客户端发一个短请求,服务端回一条持续几秒到几十秒的 token 流。请求短、响应长、方向单一——这个形状天然就是 SSE 的形状。
SSE(Server-Sent Events)本质是一个不关闭的 HTTP 响应,text/event-stream 一行一个事件。它的好处全部来自”它就是 HTTP”:
- 不用自己养连接。每次对话开一个请求,流完即走,没有心跳、没有连接池
- 断线重连是浏览器原生的。
EventSource自带 retry 和Last-Event-ID - 基础设施友好。网关、代理、鉴权中间件把它当普通 HTTP 请求,不需要为 Upgrade 开洞
实践里有一个绕不开的细节:原生 EventSource 只支持 GET、不能带自定义头。大模型请求几乎都是带 body 的 POST,所以实际用的是 fetch + ReadableStream 手动解析 event-stream 格式——多写几十行解析代码,换来完整的请求控制权,值得。
WebSocket 的账要算两本
WebSocket 给你真正的双向通道,但账要算两本。
能力账:对话场景里,客户端在响应期间要对服务端说的话其实只有一句——”停止生成”。而这句用 SSE 也能做:AbortController.abort() 掐断请求,服务端感知连接断开即停止推理。为一句话上双向通道,能力是闲置的。
运维账:这本才是大头。我在地震预警通道上维护过完整的一套:心跳保活(不发心跳,中间设备静默断你没商量)、指数退避重连、重连后的状态同步、token 过期后的连接内鉴权续期。WebSocket 不是接上就完了,它是一条要每天喂的狗。
那什么时候 WebSocket 是对的?我的判断标准:
- 服务端要主动推送与请求无关的消息(如多人协作、系统广播)
- 一条连接上要并发多路会话,靠消息里的会话 ID 分发
- 交互频繁到 HTTP 每次握手的开销真的可观
地震预警就是典型:服务端随时可能广播,客户端常驻监听——那是 WebSocket 的主场。
无论选哪个,前端都要做的事
选型只是开始,这几件事两种方案都逃不掉:
- 消息完整性:SSE 事件可能在任意字节处被截断,chunk 拼接要按分隔符切,不能假设一次
read()是一条完整消息 - 背压:渲染速度跟不上 token 速度时要攒批(见我另一篇《大模型流式渲染踩坑记》)
- 异常兜底:流中断要能区分”正常结束”、”用户停止”、”网络断开”,三种状态给用户的界面反馈完全不同
结语
技术选型最怕”哪个新用哪个”。SSE 老、朴素、单向,但大模型对话的数据流形状恰好就是它的形状。别为用不上的能力去养一条连接——这是两个项目换来的一句话。