在 ChatGPT 出现之前,大部分 Web 接口都是“请求-响应”模式:用户发一个请求,服务器处理完(可能需要几秒),然后一次性把结果扔回来。但在大模型时代,生成一段长文本可能需要 10 秒甚至更久。如果让用户盯着空白屏幕等 10 秒,体验会非常糟糕。于是,SSE (Server-Sent Events) 再次回到了聚光灯下。它允许服务器一边生成内容,一边通过长连接把数据“推”给前端,也就是我们看到的“打字机效果”。
1.什么是 SSE?
SSE(Server-Sent Events)是一种基于 HTTP 的单向通信机制。
- HTTP 标准:它不需要像 WebSocket 那样复杂的握手协议,它本质上就是一个长连接的 HTTP 请求。
- 单向通信:服务端 -> 客户端。这非常适合 AI 生成内容的场景(用户问一次,AI 持续吐字)。
- 简单易用:浏览器原生支持
EventSourceAPI,后端实现也只是多了一个yield。
2.代码展示
之前做的一个AI知识库项目需要用到SSE实现流式输出,这里展示一下SSE流式输出部分代码。
后端实现:FastAPI + sse_starlette
1 | from sse_starlette.sse import EventSourceResponse |
关键点解析:
yield: 它的作用是“产出”数据但不结束函数,函数会暂停在这里,直到下一次循环。这就实现了“生成一点,发送一点”。EventSourceResponse: 自动处理 HTTP Header(如Content-Type: text/event-stream),告诉浏览器这是一个流。
前端实现:原生 EventSource + 气泡 UI
前端部分,使用 EventSource。为了支持 Header 传参(如 Token),推荐使用 event-source-polyfill。
- 建立连接
1 | // 引入 polyfill |
- 接收数据与渲染
1 | const aiMessageDiv = document.createElement('div'); // 创建气泡 |
3.遇到的坑与解决方案
我在项目开发过程中遇到了以下问题:
- CORS 跨域问题:
- SSE 对跨域非常敏感。需要在 FastAPI 中配置
CORSMiddleware,允许前端域名访问。
- SSE 对跨域非常敏感。需要在 FastAPI 中配置
- 连接断开重连:
- 原生
EventSource会自动重连,这在聊天场景有时不需要(比如回答已经结束了)。一定要在后端发送明确的结束标记(如[[END]]),前端收到后手动.close()。
- 原生
- Nginx 缓存缓冲:
- 如果你部署在 Nginx 后面,Nginx 默认会缓存一部分响应再发送,这会导致流式效果失效(变成一卡一卡的)。需要关闭 Nginx 的
proxy_buffering。
- 如果你部署在 Nginx 后面,Nginx 默认会缓存一部分响应再发送,这会导致流式效果失效(变成一卡一卡的)。需要关闭 Nginx 的